[presets] add steamos files

This commit is contained in:
Philip Mueller
2023-07-03 14:31:13 +02:00
parent e4c9ded69f
commit 689751f754
86 changed files with 14172 additions and 0 deletions

View File

@@ -0,0 +1,256 @@
#!/bin/bash
##
## Session globals
##
export SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS=0
export HOMETEST_DESKTOP=1
export HOMETEST_DESKTOP_SESSION=plasma
export STEAM_USE_MANGOAPP=1
export MANGOHUD_CONFIGFILE=$(mktemp /tmp/mangohud.XXXXXXXX)
export MANGOAPP=1
export STEAM_USE_DYNAMIC_VRS=1
export RADV_FORCE_VRS_CONFIG_FILE=$(mktemp /tmp/radv_vrs.XXXXXXXX)
# Initially write no_display to our config file
# so we don't get mangoapp showing up before Steam initializes
# on OOBE and stuff.
mkdir -p "$(dirname "$MANGOHUD_CONFIGFILE")"
echo "no_display" > "$MANGOHUD_CONFIGFILE"
# Prepare our initial VRS config file
# for dynamic VRS in Mesa.
mkdir -p "$(dirname "$RADV_FORCE_VRS_CONFIG_FILE")"
echo "1x1" > "$RADV_FORCE_VRS_CONFIG_FILE"
# Let's try this across the board to see if it breaks anything
# Helps performance in HZD, Cyberpunk, at least
# Expose 8 physical cores, instead of 4c/8t
export WINE_CPU_TOPOLOGY=8:0,1,2,3,4,5,6,7
# To expose vram info from radv's patch we're including
export WINEDLLOVERRIDES=dxgi=n
# Workaround for steam getting killed immediatly during reboot
# See: jupiter/tasks/-/issues/280
export STEAMOS_STEAM_REBOOT_SENTINEL="/tmp/steamos-reboot-sentinel"
if [[ -e "$STEAMOS_STEAM_REBOOT_SENTINEL" ]]; then
rm -f "$STEAMOS_STEAM_REBOOT_SENTINEL"
/usr/bin/steamos-polkit-helpers/steamos-reboot-now
sudo -n reboot
fi
# Workaround for steam getting killed immediatly during shutdown
# Same idea as reboot sentinel above
export STEAMOS_STEAM_SHUTDOWN_SENTINEL="/tmp/steamos-shutdown-sentinel"
if [[ -e "$STEAMOS_STEAM_SHUTDOWN_SENTINEL" ]]; then
rm -f "$STEAMOS_STEAM_SHUTDOWN_SENTINEL"
/usr/bin/steamos-polkit-helpers/steamos-poweroff-now
sudo -n poweroff
fi
# Enable volume key management via steam for this session
export STEAM_ENABLE_VOLUME_HANDLER=1
# Have SteamRT's xdg-open send http:// and https:// URLs to Steam
export SRT_URLOPEN_PREFER_STEAM=1
# Disable automatic audio device switching in steam, now handled by wireplumber
export STEAM_DISABLE_AUDIO_DEVICE_SWITCHING=1
# Enable support for xwayland isolation per-game in Steam
export STEAM_MULTIPLE_XWAYLANDS=1
# We have the Mesa integration for the fifo-based dynamic fps-limiter
export STEAM_GAMESCOPE_DYNAMIC_FPSLIMITER=1
# Don't wait for buffers to idle on the client side before sending them to gamescope
export vk_xwayland_wait_ready=false
# Set input method modules for Qt/GTK that will show the Steam keyboard
export QT_IM_MODULE=steam
export GTK_IM_MODULE=Steam
# TODO!
# Bring this back when gamescope side is more complete
#
# Remove vsync handling from Xwayland, we handle it in gamescope
#export vblank_mode=0
#export MESA_VK_WSI_PRESENT_MODE=immediate
# To play nice with the short term callback-based limiter for now
export GAMESCOPE_LIMITER_FILE=$(mktemp /tmp/gamescope-limiter.XXXXXXXX)
export XCURSOR_THEME=steam
# Workaround for Steam login issue while Steam client change propagates out of Beta
touch ~/.steam/root/config/SteamAppData.vdf || true
export STEAM_UPDATEUI_PNG_BACKGROUND=/usr/share/steamos/steamos.png
ulimit -n 524288
# Create run directory file for startup and stats sockets
# shellcheck disable=SC2030 # (broken warning)
tmpdir="$([[ -n ${XDG_RUNTIME_DIR+x} ]] && mktemp -p "$XDG_RUNTIME_DIR" -d -t gamescope.XXXXXXX)"
socket="${tmpdir:+$tmpdir/startup.socket}"
stats="${tmpdir:+$tmpdir/stats.pipe}"
# Fail early if we don't have a proper runtime directory setup
# shellcheck disable=SC2031 # (broken warning)
if [[ -z $tmpdir || -z ${XDG_RUNTIME_DIR+x} ]]; then
echo >&2 "!! Failed to find run directory in which to create stats session sockets (is \$XDG_RUNTIME_DIR set?)"
exit 0
fi
# 1048576 = 1M - passing it like that omits the 'M' suffix - xargs removes whitespace
free_disk_space_megs=$(df ~/ --output=avail -B1048576 | sed -n '2 p' | xargs)
minimum_free_disk_space_needed_megs=500
if [[ "$free_disk_space_megs" -lt "$minimum_free_disk_space_needed_megs" ]]; then
echo >&2 "gamescope-session: not enough disk space to proceed, trying to find game to delete"
find ~/.local/share/Steam/steamapps/common/ -mindepth 1 -maxdepth 1 -type d -printf "%T@ %p\0" | sort -n -z | while IFS= read -r -d $'\0' line; do
timestamp=${line%% *}
game_folder=${line#* }
[[ -d $game_folder ]] || continue
acf=$(grep -F -- "$(basename -- "$game_folder")" ~/.local/share/Steam/steamapps/*.acf | grep \"installdir\" | cut -d: -f1)
[[ -e "$acf" ]] || continue
echo >&2 "gamescope-session: deleting $(basename "$game_folder")"
appid=$(basename "$acf" | cut -d_ -f2 | cut -d. -f1)
# TODO leave a note for Steam to display UI to explain what happened, if this logic stays
# intentionally leave compatdata; could be unclouded save files there
rm -rf --one-file-system -- "$game_folder" "$acf" ~/.local/share/Steam/steamapps/shadercache/"$appid"
free_disk_space_megs=$(df ~/ --output=avail -B1048576 | sed -n '2 p' | xargs)
[[ "$free_disk_space_megs" -lt "$minimum_free_disk_space_needed_megs" ]] || break
done
fi
export GAMESCOPE_STATS="$stats"
mkfifo -- "$stats"
mkfifo -- "$socket"
# Attempt to claim global session if we're the first one running (e.g. /run/1000/gamescope)
linkname="gamescope-stats"
# shellcheck disable=SC2031 # (broken warning)
sessionlink="${XDG_RUNTIME_DIR:+$XDG_RUNTIME_DIR/}${linkname}" # Account for XDG_RUNTIME_DIR="" (notfragileatall)
lockfile="$sessionlink".lck
exec 9>"$lockfile" # Keep as an fd such that the lock lasts as long as the session if it is taken
if flock -n 9 && rm -f "$sessionlink" && ln -sf "$tmpdir" "$sessionlink"; then
# Took the lock. Don't blow up if those commands fail, though.
echo >&2 "Claimed global gamescope stats session at \"$sessionlink\""
else
echo >&2 "!! Failed to claim global gamescope stats session"
fi
gamescope \
--generate-drm-mode fixed \
--xwayland-count 2 \
-w 1280 -h 800 \
--default-touch-mode 4 \
--hide-cursor-delay 3000 \
--max-scale 2 \
--fade-out-duration 200 \
-e -R "$socket" -T "$stats" \
-O '*',eDP-1 \
--cursor-hotspot 5,3 --cursor /usr/share/steamos/steamos-cursor.png \
&
gamescope_pid="$!"
if read -r -t 3 response_x_display response_wl_display <> "$socket"; then
export DISPLAY="$response_x_display"
export GAMESCOPE_WAYLAND_DISPLAY="$response_wl_display"
# We're done!
else
kill -9 "$gamescope_pid"
wait
exit 0
# SDDM will restart us
fi
xbindkeys -f /etc/xbindkeysrc
steamargs=("-steamos3" "-steampal" "-steamdeck" "-gamepadui")
# steamargs+=("-steamfs")
(while true; do
/usr/lib/hwsupport/power-button-handler.py
done) &
short_session_tracker_file="/tmp/steamos-short-session-tracker"
short_session_duration=60
short_session_count_before_reset=3
SECONDS=0
short_session_count=$(< "$short_session_tracker_file" wc -l)
if [[ "$short_session_count" -ge "$short_session_count_before_reset" ]]; then
# TODO could detect if we're in the devkit case below and undo that instead
# instead of resetting the wrong thing
# also could print full list of files and sizes in the Steam dir and save as a diagnostic report
echo >&2 "gamescope-session: detected broken Steam, bootstrapping from OS copy..."
mkdir -p ~/.local/share/Steam
# remove some caches and stateful things known to cause Steam to fail to start if corrupt
rm -rf --one-file-system ~/.local/share/Steam/config/widevine
# restore clean copy of binaries from RO partition
tar xf /usr/lib/steam/bootstraplinux_ubuntu12_32.tar.xz -C ~/.local/share/Steam
# rearm
rm "$short_session_tracker_file"
fi
if [[ -x $HOME/devkit-game/devkit-steam ]]; then
mangoapp &
"$HOME"/devkit-game/devkit-steam "${steamargs[@]}"
else
mangoapp &
steam "${steamargs[@]}"
fi
if [[ "$SECONDS" -lt "$short_session_duration" ]]; then
echo "frog" >> "$short_session_tracker_file"
else
rm "$short_session_tracker_file"
fi
# Ask gamescope to exit nicely
kill $gamescope_pid
# Start a background sleep for five seconds because we don't trust it
sleep 5 &
sleep_pid=$!
# Wait for gamescope or the sleep to finish for timeout purposes
ret=0
wait -n $gamescope_pid $sleep_pid || ret=$?
# If we get a SIGTERM/etc while waiting this happens. Proceed to kill -9 everything but complain
if [[ $ret = 127 ]]; then
echo >&2 "gamescope-session: Interrupted while waiting on teardown, force-killing remaining tasks"
fi
# Kill all remaining jobs and warn if unexpected things are in there (should be just sleep_pid, unless gamescope failed
# to exit in time or we hit the interrupt case above)
for job in $(jobs -p); do
# Warn about unexpected things
if [[ $ret != 127 && $job = "$gamescope_pid" ]]; then
echo >&2 "gamescope-session: gamescope timed out while exiting, killing"
elif [[ $ret != 127 && $job != "$sleep_pid" ]]; then
echo >&2 "gamescope-session: unexpected background pid $job at teardown: "
# spew some debug about it
ps -p "$job" >&2
fi
kill -9 "$job"
done
# This should just be waiting on kill -9'd things. Another signal will cause us to give up, but we should be a little
# stubborn about not letting the session die with gamescope holding on to things.
wait

View File

@@ -0,0 +1,12 @@
#!/bin/bash
set -eu
TARGETS=('/bin/bash /usr/bin/gamescope-session'
'/usr/bin/kwin_x11')
for target in "${TARGETS[@]}"; do
for processtree in $(pgrep -xf "$target" || true); do
kill -- "-$processtree"
done
done

View File

@@ -0,0 +1,246 @@
#!/usr/bin/bash -e
#
# pacman-db-upgrade - upgrade the local pacman db to a newer format
#
# Copyright (c) 2010-2021 Pacman Development Team <pacman-dev@archlinux.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Avoid creating world-unreadable files
umask 022
# gettext initialization
export TEXTDOMAIN='pacman-scripts'
export TEXTDOMAINDIR='/usr/share/locale'
declare -r myver='6.0.1'
LIBRARY=${LIBRARY:-'/usr/share/makepkg'}
# Import libmakepkg
source "$LIBRARY"/util/message.sh
source "$LIBRARY"/util/parseopts.sh
usage() {
printf "pacman-db-upgrade (pacman) %s\n" "${myver}"
echo
printf -- "$(gettext "Upgrade the local pacman database to a newer format")\n"
echo
printf -- "$(gettext "Usage: %s [options]")\n" "$0"
echo
printf -- "$(gettext "options:")\n"
printf -- "$(gettext " -d, --dbpath <path> set an alternate database location")\n"
printf -- "$(gettext " -h, --help show this help message and exit")\n"
printf -- "$(gettext " -r, --root <path> set an alternate installation root")\n"
printf -- "$(gettext " -V, --version show version information and exit")\n"
printf -- "$(gettext " --config <path> set an alternate configuration file")\n"
printf -- "$(gettext " --nocolor disable colorized output messages")\n"
echo
}
version() {
printf "pacman-db-upgrade (pacman) %s\n" "$myver"
printf -- "Copyright (c) 2010-2021 Pacman Development Team <pacman-dev@archlinux.org>.\n"
printf '\n'
printf -- "$(gettext "\
This is free software; see the source for copying conditions.\n\
There is NO WARRANTY, to the extent permitted by law.\n")"
}
die() {
error "$@"
exit 1
}
die_r() {
rm -f "$lockfile"
die "$@"
}
resolve_dir() {
local d="$(cd "$1"; pwd -P)"
[[ $d == */ ]] || d+=/
printf "%s" "$d"
}
# PROGRAM START
# determine whether we have gettext; make it a no-op if we do not
if ! type gettext &>/dev/null; then
gettext() {
echo "$@"
}
fi
USE_COLOR='y'
OPT_SHORT="d:hr:V"
OPT_LONG=('config:' 'dbpath:' 'help' 'nocolor' 'root:' 'version')
if ! parseopts "$OPT_SHORT" "${OPT_LONG[@]}" -- "$@"; then
exit 1 # E_INVALID_OPTION
fi
set -- "${OPTRET[@]}"
unset OPT_SHORT OPT_LONG OPTRET
while true; do
case "$1" in
--config) shift; conffile="$1" ;;
-d|--dbpath) shift; dbroot="$1" ;;
-r|--root) shift; pacroot="$1" ;;
-h|--help) usage; exit 0 ;;
--nocolor) USE_COLOR='n' ;;
-V|--version) version; exit 0 ;;
--) shift; break ;;
esac
shift
done
conffile=${conffile:-/etc/pacman.conf}
[[ -z $pacroot ]] && pacroot=$(pacman-conf --config="$conffile" rootdir)
[[ -z $dbroot ]] && dbroot=$(pacman-conf --config="$conffile" --rootdir="$pacroot" dbpath)
# check if messages are to be printed using color
if [[ -t 2 && $USE_COLOR != "n" ]]; then
colorize
else
unset ALL_OFF BOLD BLUE GREEN RED YELLOW
fi
if [[ ! -d $dbroot ]]; then
die "$(gettext "%s does not exist or is not a directory.")" "$dbroot"
fi
if [[ ! -d $dbroot/local ]]; then
die "$(gettext "%s is not a pacman database directory.")" "$dbroot"
fi
if [[ ! -w $dbroot ]]; then
die "$(gettext "You must have correct permissions to upgrade the database.")"
fi
# strip any trailing slash from our dbroot
dbroot="${dbroot%/}"
# form the path to our lockfile location
lockfile="${dbroot}/db.lck"
# make sure pacman isn't running
if [[ -f $lockfile ]]; then
die "$(gettext "Pacman lock file was found. Cannot run while pacman is running.")"
fi
# do not let pacman run while we do this
touch "$lockfile"
if [[ -f "${dbroot}"/local/ALPM_DB_VERSION ]]; then
db_version=$(cat "${dbroot}"/local/ALPM_DB_VERSION)
fi
if [[ -z "$db_version" ]]; then
# pacman-3.4 to 3.5 upgrade - merge depends into desc
if [[ $(find "$dbroot"/local -name depends) ]]; then
msg "$(gettext "Pre-3.5 database format detected - upgrading...")"
for i in "$dbroot"/local/*; do
if [[ -f "$i"/depends ]]; then
cat "$i"/depends >> "$i"/desc
rm "$i"/depends
fi
done
msg "$(gettext "Done.")"
fi
# pacman 4.1 to 4.2 upgrade - remove directory symlink support
msg "$(gettext "Pre-4.2 database format detected - upgrading...")"
dirlist=()
unset GREP_OPTIONS
while IFS= read -r dir; do
dirlist+=("${pacroot}${dir%/}")
done < <(grep -h '/$' "$dbroot"/local/*/files | sort -ru)
mapfile -t dirlist < <(
for dir in "${dirlist[@]}"; do
[[ -L "$dir" ]] && echo "$dir"
done)
if [[ ${#dirlist[@]} != 0 ]]; then
pacroot="$(resolve_dir "$pacroot")"
for dir in "${dirlist[@]}"; do
realdir="$(resolve_dir "$dir")"
# verify realdir is inside root
if [[ ${realdir:0:${#pacroot}} != "$pacroot" ]]; then
warning "$(gettext "symlink '%s' points outside pacman root, manual repair required")" "$dir"
continue
fi
# convert to an appropriate form for the replacement
olddir="${dir:${#pacroot}}/"
newdir="${realdir:${#pacroot}}"
# construct the parents of the new directory
parents=""
parent="$(dirname "$newdir")"
while [[ $parent != "." ]]; do
parents+="$parent/\n"
parent="$(dirname "$parent")"
done
for f in "$dbroot"/local/*/files; do
awk -v "olddir=$olddir" -v "newdir=$newdir" -v "parents=$parents" '
function print_path(path) {
if (path != "" && !(path in seen)) {
seen[path] = 1
print path
}
}
BEGIN {
oldlen = length(olddir) + 1
file = substr(newdir, 0, length(newdir) - 1)
}
{
if ($0 == "") {
# end of section, clear seen paths and print as-is
for ( i in seen ) {
delete seen[i]
}
print
} else if ($0 == olddir) {
# replace symlink with its target, including parents
split(parents, paths, "\n")
for (i in paths) {
print_path(paths[i])
}
print_path(newdir)
} else if ($0 == file) {
# newdir already existed as a file, skip it
} else if (index($0, olddir) == 1) {
# update paths that were under olddir
print_path(newdir substr($0, oldlen))
} else {
# print everything else as-is
print_path($0)
}
}' "$f" > "$f.tmp"
mv "$f.tmp" "$f"
done
done
fi
fi
echo "9" > "$dbroot"/local/ALPM_DB_VERSION
# remove the lock file
rm -f "$lockfile"

View File

@@ -0,0 +1,741 @@
#!/usr/bin/bash
#
# pacman-key - manages pacman's keyring
# Based on apt-key, from Debian
#
# Copyright (c) 2010-2021 Pacman Development Team <pacman-dev@archlinux.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# gettext initialization
export TEXTDOMAIN='pacman-scripts'
export TEXTDOMAINDIR='/usr/share/locale'
declare -r myver="6.0.1"
LIBRARY=${LIBRARY:-'/usr/share/makepkg'}
# Import libmakepkg
source "$LIBRARY"/util/message.sh
source "$LIBRARY"/util/parseopts.sh
# Options
ADD=0
DELETE=0
EDITKEY=0
EXPORT=0
FINGER=0
IMPORT=0
IMPORT_TRUSTDB=0
INIT=0
KEYSERVER=''
LISTKEYS=0
LISTSIGS=0
LSIGNKEY=0
POPULATE=0
RECEIVE=0
REFRESH=0
UPDATEDB=0
USE_COLOR='y'
VERIFY=0
VERBOSE=0
usage() {
printf "pacman-key (pacman) %s\n" ${myver}
echo
printf -- "$(gettext "Usage: %s [options] operation [targets]")\n" $(basename $0)
echo
printf -- "$(gettext "Manage pacman's list of trusted keys")\n"
echo
printf -- "$(gettext "Operations:")\n"
printf -- "$(gettext " -a, --add Add the specified keys (empty for stdin)")\n"
printf -- "$(gettext " -d, --delete Remove the specified keyids")\n"
printf -- "$(gettext " -e, --export Export the specified or all keyids")\n"
printf -- "$(gettext " -f, --finger List fingerprint for specified or all keyids")\n"
printf -- "$(gettext " -l, --list-keys List the specified or all keys")\n"
printf -- "$(gettext " -r, --recv-keys Fetch the specified keyids")\n"
printf -- "$(gettext " -u, --updatedb Update the trustdb of pacman")\n"
printf -- "$(gettext " -v, --verify Verify the file(s) specified by the signature(s)")\n"
printf -- "$(gettext " --edit-key Present a menu for key management task on keyids")\n"
printf -- "$(gettext " --import Imports pubring.gpg from dir(s)")\n"
printf -- "$(gettext " --import-trustdb Imports ownertrust values from trustdb.gpg in dir(s)")\n"
printf -- "$(gettext " --init Ensure the keyring is properly initialized")\n"
printf -- "$(gettext " --list-sigs List keys and their signatures")\n"
printf -- "$(gettext " --lsign-key Locally sign the specified keyid")\n"
printf -- "$(gettext " --populate Reload the default keys from the (given) keyrings\n\
in '%s'")\n" "/usr/share/pacman/keyrings"
printf -- "$(gettext " --refresh-keys Update specified or all keys from a keyserver")\n"
printf -- "$(gettext " --verbose Show extra information")\n"
echo
printf -- "$(gettext "Options:")\n"
printf -- "$(gettext " --config <file> Use an alternate config file (instead of\n\
'%s')")\n" "/etc/pacman.conf"
printf -- "$(gettext " --gpgdir <dir> Set an alternate directory for GnuPG (instead\n\
of '%s')")\n" "/etc/pacman.d/gnupg"
printf -- "$(gettext " --keyserver <server-url> Specify a keyserver to use if necessary")\n"
echo
printf -- "$(gettext " -h, --help Show this help message and exit")\n"
printf -- "$(gettext " -V, --version Show program version")\n"
}
version() {
printf "pacman-key (pacman) %s\n" "${myver}"
printf -- "Copyright (c) 2010-2021 Pacman Development Team <pacman-dev@archlinux.org>.\n"
printf '\n'
printf -- "$(gettext "\
This is free software; see the source for copying conditions.\n\
There is NO WARRANTY, to the extent permitted by law.\n")"
}
# read the config file "$1" which has key=value pairs, and return the key which
# matches "$2". the equals sign between pairs may be surrounded by any amount
# of whitespace. Optionally, "$3" can be specified which is the default value
# when no key is found.
get_from() {
while IFS='=' read -r key value; do
[[ -z $key || ${key:0:1} = '#' ]] && continue
if [[ ${key%% *} = "$2" && -n ${value##* } ]]; then
echo "${value##* }"
return 0
fi
done < "$1"
if [[ -n "$3" ]]; then
printf '%s\n' "$3"
return 0
fi
return 1
}
key_lookup_from_name() {
local ids
mapfile -t ids < \
<("${GPG_PACMAN[@]}" --search-keys --batch --with-colons "$1" 2>/dev/null |
awk -F: '$1 == "pub" { print $2 }')
# only return success on non-ambiguous lookup
case ${#ids[*]} in
0)
error "$(gettext "Failed to lookup key by name:") %s" "$name"
return 1
;;
1)
printf '%s' "${ids[0]}"
return 0
;;
*)
error "$(gettext "Key name is ambiguous:") %s" "$name"
return 1
;;
esac
}
generate_master_key() {
# Generate the master key, which will be in both pubring and secring
msg "$(gettext "Generating pacman master key. This may take some time.")"
"${GPG_PACMAN[@]}" --gen-key --batch <<EOF
%echo Generating pacman keyring master key...
Key-Type: RSA
Key-Length: 4096
Key-Usage: sign
Name-Real: Pacman Keyring Master Key
Name-Email: pacman@localhost
Expire-Date: 0
%no-protection
%commit
%echo Done
EOF
}
secret_keys_available() {
"${GPG_PACMAN[@]}" -K --with-colons | wc -l
}
# Adds the given gpg.conf option if it is not present in the file.
# Note that if we find it commented out, we won't add the option.
# args: $1 conffile, $2 option-name, $3 (optional) option-value
add_gpg_conf_option() {
local conffile=$1; shift
# looking for the option 'bare', only leading spaces or # chars allowed,
# followed by at least one space and any other text or the end of line.
if ! grep -q "^[[:space:]#]*$*\([[:space:]].*\)*$" "$conffile" &>/dev/null; then
printf '%s\n' "$*" >> "$conffile"
fi
}
check_keyids_exist() {
local ret=0
for key in "$@"; do
# Verify if the key exists in pacman's keyring
if ! "${GPG_PACMAN[@]}" --list-keys "$key" &>/dev/null ; then
error "$(gettext "The key identified by %s could not be found locally.")" "$key"
ret=1
fi
done
if (( ret )); then
exit 1
fi
}
key_is_lsigned() {
secret_key=$("${GPG_PACMAN[@]}" --with-colons --list-secret-key --quiet | awk -F : 'NR==1 {print $5}')
while IFS=: read -r type valid _ _ sign_key _; do
if [[ $type != "sig" || $valid != "!" ]]; then
continue
fi
if [[ "$sign_key" == "$secret_key" ]]; then
return 0
fi
done < <("${GPG_PACMAN[@]}" --with-colons --check-signatures --quiet "$1")
return 1
}
key_is_revoked() {
while IFS=: read -r type _ _ _ _ _ _ _ _ _ _ flags _; do
if [[ $type != "pub" ]]; then
continue
fi
if [[ $flags == *"D"* ]]; then
return 0
fi
done < <("${GPG_PACMAN[@]}" --with-colons --list-key --quiet "$1")
return 1
}
initialize() {
local conffile keyserv
# Check for simple existence rather than for a directory as someone
# may want to use a symlink here
[[ -e ${PACMAN_KEYRING_DIR} ]] || mkdir -p -m 755 "${PACMAN_KEYRING_DIR}"
# keyring files
[[ -f ${PACMAN_KEYRING_DIR}/pubring.gpg ]] || touch ${PACMAN_KEYRING_DIR}/pubring.gpg
[[ -f ${PACMAN_KEYRING_DIR}/secring.gpg ]] || touch ${PACMAN_KEYRING_DIR}/secring.gpg
[[ -f ${PACMAN_KEYRING_DIR}/trustdb.gpg ]] || "${GPG_PACMAN[@]}" --update-trustdb
chmod 644 ${PACMAN_KEYRING_DIR}/{pubring,trustdb}.gpg
chmod 600 ${PACMAN_KEYRING_DIR}/secring.gpg
# gpg.conf
conffile="${PACMAN_KEYRING_DIR}/gpg.conf"
[[ -f $conffile ]] || touch "$conffile"
chmod 644 "$conffile"
add_gpg_conf_option "$conffile" 'no-greeting'
add_gpg_conf_option "$conffile" 'no-permission-warning'
add_gpg_conf_option "$conffile" 'lock-never'
add_gpg_conf_option "$conffile" 'keyserver-options' 'timeout=10'
add_gpg_conf_option "$conffile" 'keyserver-options' 'import-clean'
local gpg_ver=$(gpg --version | awk '{print $3; exit}')
if (( $(vercmp "$gpg_ver" 2.2.17) >= 0 )); then
add_gpg_conf_option "$conffile" 'keyserver-options' 'no-self-sigs-only'
fi
# gpg-agent.conf
agent_conffile="${PACMAN_KEYRING_DIR}/gpg-agent.conf"
[[ -f $agent_conffile ]] || touch "$agent_conffile"
chmod 644 "$agent_conffile"
add_gpg_conf_option "$agent_conffile" 'disable-scdaemon'
# set up a private signing key (if none available)
if [[ $(secret_keys_available) -lt 1 ]]; then
generate_master_key
UPDATEDB=1
fi
}
check_keyring() {
if [[ ! -r ${PACMAN_KEYRING_DIR}/pubring.gpg || \
! -r ${PACMAN_KEYRING_DIR}/trustdb.gpg ]]; then
error "$(gettext "You do not have sufficient permissions to read the %s keyring.")" "pacman"
msg "$(gettext "Use '%s' to correct the keyring permissions.")" "pacman-key --init"
exit 1
fi
if (( (EXPORT || FINGER || LIST || VERIFY) && EUID != 0 )); then
if ! grep -q "^[[:space:]]*lock-never[[:space:]]*$" ${PACMAN_KEYRING_DIR}/gpg.conf &>/dev/null; then
error "$(gettext "You do not have sufficient permissions to run this command.")"
msg "$(gettext "Use '%s' to correct the keyring permissions.")" "pacman-key --init"
exit 1
fi
fi
if (( LSIGNKEY || POPULATE )); then
if [[ $(secret_keys_available) -lt 1 ]]; then
error "$(gettext "There is no secret key available to sign with.")"
msg "$(gettext "Use '%s' to generate a default secret key.")" "pacman-key --init"
exit 1
fi
fi
}
populate_keyring() {
local KEYRING_IMPORT_DIR='/usr/share/pacman/keyrings'
local keyring KEYRINGIDS=("$@")
local ret=0
if (( ${#KEYRINGIDS[*]} == 0 )); then
# get list of all available keyrings
shopt -s nullglob
KEYRINGIDS=("$KEYRING_IMPORT_DIR"/*.gpg)
shopt -u nullglob
KEYRINGIDS=("${KEYRINGIDS[@]##*/}")
KEYRINGIDS=("${KEYRINGIDS[@]%.gpg}")
if (( ${#KEYRINGIDS[*]} == 0 )); then
error "$(gettext "No keyring files exist in %s.")" "$KEYRING_IMPORT_DIR"
ret=1
fi
else
# verify listed keyrings exist
for keyring in "${KEYRINGIDS[@]}"; do
if [[ ! -f "$KEYRING_IMPORT_DIR/$keyring.gpg" ]]; then
error "$(gettext "The keyring file %s does not exist.")" "$KEYRING_IMPORT_DIR/$keyring.gpg"
ret=1
fi
done
fi
if (( ret )); then
exit 1
fi
# Variable used for iterating on keyrings
local keys key_id
# Add keys from requested keyrings
for keyring in "${KEYRINGIDS[@]}"; do
msg "$(gettext "Appending keys from %s.gpg...")" "$keyring"
"${GPG_PACMAN[@]}" --quiet --import "${KEYRING_IMPORT_DIR}/${keyring}.gpg"
done
# Read the trusted key IDs to an array. Because this is an ownertrust
# file, we know we have the full 40 hex digit fingerprint values.
# Format of ownertrust dump file:
# 40CHARFINGERPRINTXXXXXXXXXXXXXXXXXXXXXXX:6:
# 40CHARFINGERPRINTXXXXXXXXXXXXXXXXXXXXXXX:5:
local -A trusted_ids
for keyring in "${KEYRINGIDS[@]}"; do
if [[ -s "${KEYRING_IMPORT_DIR}/${keyring}-trusted" ]]; then
while IFS=: read key_id _; do
# skip blank lines, comments; these are valid in this file
[[ -z $key_id || ${key_id:0:1} = \# ]] && continue
if key_is_lsigned "$key_id" ; then
continue
fi
# Mark this key to be lsigned
trusted_ids[$key_id]=$keyring
done < "${KEYRING_IMPORT_DIR}/${keyring}-trusted"
fi
done
local -A revoked_ids
for keyring in "${KEYRINGIDS[@]}"; do
if [[ -s $KEYRING_IMPORT_DIR/$keyring-revoked ]]; then
while read -r key_id; do
if key_is_revoked "$key_id" ; then
continue
fi
revoked_ids["$key_id"]=1
done <"$KEYRING_IMPORT_DIR/$keyring-revoked"
fi
done
if (( ${#trusted_ids[@]} > 0 )); then
msg "$(gettext "Locally signing trusted keys in keyring...")"
lsign_keys "${!trusted_ids[@]}"
msg "$(gettext "Importing owner trust values...")"
for keyring in "${KEYRINGIDS[@]}"; do
if [[ -s "${KEYRING_IMPORT_DIR}/${keyring}-trusted" ]]; then
"${GPG_PACMAN[@]}" --import-ownertrust "${KEYRING_IMPORT_DIR}/${keyring}-trusted"
fi
done
fi
if (( ${#revoked_ids[@]} > 0 )); then
local key_count=0
msg "$(gettext "Disabling revoked keys in keyring...")"
for key_id in "${!revoked_ids[@]}"; do
if (( VERBOSE )); then
msg2 "$(gettext "Disabling key %s...")" "${key_id}"
fi
printf 'disable\nquit\n' | LANG=C "${GPG_PACMAN[@]}" --command-fd 0 --quiet --batch --edit-key "${key_id}" 2>/dev/null
key_count=$((key_count+1))
done
if (( key_count )); then
msg2 "$(gettext "Disabled %s keys.")" "${key_count}"
fi
fi
}
add_keys() {
if ! "${GPG_PACMAN[@]}" --quiet --batch --import "$@" ; then
error "$(gettext "A specified keyfile could not be added to the keyring.")"
exit 1
fi
}
delete_keys() {
check_keyids_exist "$@"
if ! "${GPG_PACMAN[@]}" --quiet --batch --delete-key --yes "$@" ; then
error "$(gettext "A specified key could not be removed from the keyring.")"
exit 1
fi
}
edit_keys() {
check_keyids_exist "$@"
local ret=0
for key in "$@"; do
if ! "${GPG_PACMAN[@]}" --edit-key "$key" ; then
error "$(gettext "The key identified by %s could not be edited.")" "$key"
ret=1
fi
done
if (( ret )); then
exit 1
fi
}
export_keys() {
check_keyids_exist "$@"
if ! "${GPG_PACMAN[@]}" --armor --export "$@" ; then
error "$(gettext "A specified key could not be exported from the keyring.")"
exit 1
fi
}
finger_keys() {
check_keyids_exist
if ! "${GPG_PACMAN[@]}" --batch --fingerprint "$@" ; then
error "$(gettext "The fingerprint of a specified key could not be determined.")"
exit 1
fi
}
import_trustdb() {
local importdir
local ret=0
for importdir in "$@"; do
if [[ -f "${importdir}/trustdb.gpg" ]]; then
gpg --homedir "${importdir}" --export-ownertrust | \
"${GPG_PACMAN[@]}" --import-ownertrust -
if (( PIPESTATUS )); then
error "$(gettext "%s could not be imported.")" "${importdir}/trustdb.gpg"
ret=1
fi
else
error "$(gettext "File %s does not exist and could not be imported.")" "${importdir}/trustdb.gpg"
ret=1
fi
done
if (( ret )); then
exit 1
fi
}
import() {
local importdir
local ret=0
for importdir in "$@"; do
if [[ -f "${importdir}/pubring.gpg" ]]; then
if ! "${GPG_PACMAN[@]}" --quiet --batch --import "${importdir}/pubring.gpg" ; then
error "$(gettext "%s could not be imported.")" "${importdir}/pubring.gpg"
ret=1
fi
else
error "$(gettext "File %s does not exist and could not be imported.")" "${importdir}/pubring.gpg"
ret=1
fi
done
if (( ret )); then
exit 1
fi
}
list_keys() {
check_keyids_exist
if ! "${GPG_PACMAN[@]}" --batch --list-keys "$@" ; then
error "$(gettext "A specified key could not be listed.")"
exit 1
fi
}
list_sigs() {
check_keyids_exist
if ! "${GPG_PACMAN[@]}" --batch --list-sigs "$@" ; then
error "$(gettext "A specified signature could not be listed.")"
exit 1
fi
}
lsign_keys() {
check_keyids_exist
local ret=0
local key_count=0
for key_id in "$@"; do
if (( VERBOSE )); then
msg2 "$(gettext "Locally signing key %s...")" "${key_id}"
fi
# we cannot use --yes here as gpg would still ask for confirmation if a key has more than one uid
printf 'y\ny\n' | LANG=C "${GPG_PACMAN[@]}" --command-fd 0 --quiet --batch --lsign-key "${key_id}" 2>/dev/null
if (( PIPESTATUS[1] )); then
error "$(gettext "%s could not be locally signed.")" "${key_id}"
ret=1
fi
key_count=$((key_count+1))
done
if (( ret )); then
exit 1
fi
if (( key_count )); then
msg2 "$(gettext "Locally signed %s keys.")" "${key_count}"
fi
}
receive_keys() {
local ret=0 name id keyids emails
# if the key is not a hex ID, do a lookup
for name; do
if [[ $name = ?(0x)+([0-9a-fA-F]) ]]; then
keyids+=("$name")
elif [[ $name = *@*.* ]]; then
emails+=("$name")
elif id=$(key_lookup_from_name "$name"); then
keyids+=("$id")
fi
done
(( ${#keyids[*]}+${#emails[*]} > 0 )) || exit 1
if (( ${#emails[*]} > 0 )) && \
! "${GPG_PACMAN[@]}" --auto-key-locate clear,nodefault,wkd,keyserver \
--locate-key "${emails[@]}" ; then
error "$(gettext "Remote key not fetched correctly from WKD or keyserver.")"
ret=1
fi
if (( ${#keyids[*]} > 0 )) && ! "${GPG_PACMAN[@]}" --recv-keys "${keyids[@]}" ; then
error "$(gettext "Remote key not fetched correctly from keyserver.")"
ret=1
fi
exit $ret
}
refresh_keys() {
local ret=0 ids masterkey emails
check_keyids_exist "$@"
# don't try to refresh the user's local masterkey
masterkey="$("${GPG_PACMAN[@]}" --list-keys --with-colons pacman@localhost |
awk -F: '$1 == "pub" { print $5 }')"
mapfile -t ids < \
<("${GPG_PACMAN[@]}" --list-keys --with-colons "$@" |
awk -F: '$1 == "pub" { print $5 }' | grep -vx "$masterkey")
for id in "${ids[@]}"; do
mapfile -t emails < \
<("${GPG_PACMAN[@]}" --list-keys --list-options show-only-fpr-mbox "$id" |
awk '{print $2 }')
# first try looking up the key in a WKD (only works by email address)
for email in "${emails[@]}"; do
"${GPG_PACMAN[@]}" --locate-external-keys "$email" && break
done
# if no key was found, fall back to using the keyservers (with the key fingerprint instead)
if (( $? )) && ! "${GPG_PACMAN[@]}" --refresh-keys "$id"; then
error "$(gettext "Could not update key: %s")" "$id"
ret=1
fi
done
exit $ret
}
verify_sig() {
local ret=0 sig=$1 file=$2
if [[ -z $file && -f ${sig%.*} ]]; then
file=${sig%.*}
fi
if [[ -n $file ]]; then
local files=("$sig" "$file")
msg "Checking %s... (detached)" "$sig"
else
local files=("$sig")
msg "Checking %s... (embedded)" "$sig"
fi
if grep -q 'BEGIN PGP SIGNATURE' "$sig"; then
error "$(gettext "Cannot use armored signatures for packages: %s")" "$sig"
exit 1
fi
if ! "${GPG_PACMAN[@]}" --status-fd 1 --verify "${files[@]}" | grep -qE '^\[GNUPG:\] TRUST_(FULLY|ULTIMATE).*$'; then
error "$(gettext "The signature identified by %s could not be verified.")" "$sig"
ret=1
fi
exit $ret
}
updatedb() {
msg "$(gettext "Updating trust database...")"
if ! "${GPG_PACMAN[@]}" --batch --check-trustdb ; then
error "$(gettext "Trust database could not be updated.")"
exit 1
fi
}
# PROGRAM START
if ! type gettext &>/dev/null; then
gettext() {
echo "$@"
}
fi
OPT_SHORT="adefhlruvV"
OPT_LONG=('add' 'config:' 'delete' 'edit-key' 'export' 'finger' 'gpgdir:'
'help' 'import' 'import-trustdb' 'init' 'keyserver:' 'list-keys' 'list-sigs'
'lsign-key' 'nocolor' 'populate' 'recv-keys' 'refresh-keys' 'updatedb'
'verbose' 'verify' 'version')
if ! parseopts "$OPT_SHORT" "${OPT_LONG[@]}" -- "$@"; then
exit 1 # E_INVALID_OPTION
fi
set -- "${OPTRET[@]}"
unset OPT_SHORT OPT_LONG OPTRET
if [[ $1 == "--" ]]; then
usage
exit 0
fi
while (( $# )); do
case $1 in
-a|--add) ADD=1 UPDATEDB=1 ;;
--config) shift; CONFIG=$1 ;;
-d|--delete) DELETE=1 UPDATEDB=1 ;;
--edit-key) EDITKEY=1 UPDATEDB=1 ;;
-e|--export) EXPORT=1 ;;
-f|--finger) FINGER=1 ;;
--gpgdir) shift; PACMAN_KEYRING_DIR=$1 ;;
--import) IMPORT=1 UPDATEDB=1 ;;
--import-trustdb) IMPORT_TRUSTDB=1 UPDATEDB=1 ;;
--init) INIT=1 ;;
--keyserver) shift; KEYSERVER=$1 ;;
-l|--list-keys) LISTKEYS=1 ;;
--list-sigs) LISTSIGS=1 ;;
--lsign-key) LSIGNKEY=1 UPDATEDB=1 ;;
--nocolor) USE_COLOR='n';;
--populate) POPULATE=1 UPDATEDB=1 ;;
-r|--recv-keys) RECEIVE=1 UPDATEDB=1 ;;
--refresh-keys) REFRESH=1 ;;
-u|--updatedb) UPDATEDB=1 ;;
--verbose) VERBOSE=1 ;;
-v|--verify) VERIFY=1 ;;
-h|--help) usage; exit 0 ;;
-V|--version) version; exit 0 ;;
--) shift; break ;;
esac
shift
done
# check if messages are to be printed using color
if [[ -t 2 && $USE_COLOR != "n" ]]; then
colorize
else
unset ALL_OFF BOLD BLUE GREEN RED YELLOW
fi
if ! type -p gpg >/dev/null; then
error "$(gettext "Cannot find the %s binary required for all %s operations.")" "gpg" "pacman-key"
exit 1
fi
if (( (ADD || DELETE || EDITKEY || IMPORT || IMPORT_TRUSTDB || INIT || LSIGNKEY || POPULATE || RECEIVE || REFRESH || UPDATEDB) && EUID != 0 )); then
error "$(gettext "%s needs to be run as root for this operation.")" "pacman-key"
exit 1
fi
CONFIG=${CONFIG:-/etc/pacman.conf}
if [[ ! -r "${CONFIG}" ]]; then
error "$(gettext "%s configuration file '%s' not found.")" "pacman" "$CONFIG"
exit 1
fi
# if PACMAN_KEYRING_DIR isn't assigned, try to get it from the config
# file, falling back on a hard default
PACMAN_KEYRING_DIR=${PACMAN_KEYRING_DIR:-$(pacman-conf --config="$CONFIG" gpgdir)}
GPG_PACMAN=(gpg --homedir "${PACMAN_KEYRING_DIR}" --no-permission-warning)
if [[ -n ${KEYSERVER} ]]; then
GPG_PACMAN+=(--keyserver "${KEYSERVER}")
fi
# check only a single operation has been given
# don't include UPDATEDB in here as other opts can induce it
numopt=$(( ADD + DELETE + EDITKEY + EXPORT + FINGER + IMPORT + IMPORT_TRUSTDB +
INIT + LISTKEYS + LISTSIGS + LSIGNKEY + POPULATE + RECEIVE + REFRESH + VERIFY ))
case $numopt in
0)
if (( ! UPDATEDB )); then
error "$(gettext "no operation specified (use -h for help)")"
exit 1
fi
;;
[!1])
error "$(gettext "Multiple operations specified.")"
msg "$(gettext "Please run %s with each operation separately.")" "pacman-key"
exit 1
;;
esac
# check for targets where needed
if (( (ADD || DELETE || EDIT || IMPORT || IMPORT_TRUSTDB ||
LSIGNKEY || RECEIVE || VERIFY) && $# == 0 )); then
error "$(gettext "No targets specified")"
exit 1
fi
(( ! INIT )) && check_keyring
(( ADD )) && add_keys "$@"
(( DELETE )) && delete_keys "$@"
(( EDITKEY )) && edit_keys "$@"
(( EXPORT )) && export_keys "$@"
(( FINGER )) && finger_keys "$@"
(( IMPORT )) && import "$@"
(( IMPORT_TRUSTDB)) && import_trustdb "$@"
(( INIT )) && initialize
(( LISTKEYS )) && list_keys "$@"
(( LISTSIGS )) && list_sigs "$@"
(( LSIGNKEY )) && lsign_keys "$@"
(( POPULATE )) && populate_keyring "$@"
(( RECEIVE )) && receive_keys "$@"
(( REFRESH )) && refresh_keys "$@"
(( VERIFY )) && verify_sig "$@"
(( UPDATEDB )) && updatedb
exit 0

View File

@@ -0,0 +1,242 @@
#!/bin/bash
#
# Copyright (c) 2020 Gaël PORTAY
#
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# https://www.freedesktop.org/software/systemd/man/systemd.offline-updates.html
set -e
set -u
set -o pipefail
VERSION="1.1"
opts=(--noconfirm --noprogressbar --cachedir /var/lib/system-update)
while [[ "$#" -ne 0 ]]
do
if [[ "$1" =~ (-h|--help) ]]
then
echo "Usage: ${0##*/} [--now] [--force] [--] [pacman-extra-flags]"
exit 0
elif [[ "$1" =~ (-V|--version) ]]
then
echo "$VERSION"
exit
elif [[ "$1" == "--now" ]]
then
now=true
elif [[ "$1" == "--force" ]]
then
force=true
elif [[ "$1" == "--" ]]
then
shift
break
else
echo "$1: Invalid argument" >&2
exit 1
fi
shift
done
if [[ -z "${now:-}" ]]
then
if [[ ! -e /system-update ]] || [[ "${force:-}" ]]
then
echo ":: Preparing offline system updates..."
mkdir -p /var/lib/system-update
pacman "${opts[@]}" --downloadonly -Syu "$@"
mapfile -t pkgs < <(find /var/lib/system-update -name "*.pkg.tar.*")
if [[ "${#pkgs[@]}" -eq 0 ]]
then
exit 0
fi
ln -sf /var/lib/system-update /system-update
if [[ "$#" -gt 0 ]]
then
echo "$@" >/var/lib/system-update/.flags
fi
fi
echo ":: Please reboot the machine to trigger the offline system updates."
if [[ -e /var/lib/system-update/.flags ]]
then
cat /var/lib/system-update/.flags
fi
exit
fi
# As the first step, an update service should check if the /system-update
# symlink points to the location used by that update service. In case it does
# not exist or points to a different location, the service must exit without
# error.
if [[ ! -L /system-update ]] ||
[[ "$(readlink -f /system-update)" != "/var/lib/system-update" ]]
then
echo ":: No offline system updates prepared."
exit 0
fi
# Make sure to remove the /system-update symlink as early as possible in the
# update script to avoid reboot loops in case the update fails.
rm -f /system-update
progress() {
if plymouth --ping 2>/dev/null
then
plymouth system-update --progress="$1"
fi
echo -n "$1 "
}
if [[ -r /var/lib/system-update/.flags ]]
then
read -r -a extraflags < <(cat /var/lib/system-update/.flags)
if [[ "${#extraflags[@]}" -gt 0 ]]
then
set -- "$@" "${extraflags[@]}"
fi
rm -f /var/lib/system-update/.flags
fi
# If your script succeeds you should trigger the reboot in your own code, for
# example by invoking logind's Reboot() call or calling systemctl reboot. See
# logind dbus API for details.
# shellcheck disable=SC2154
trap 'ret="$?"; trap - 0; if [ "$ret" -eq 0 ]; then systemctl reboot; return "$ret"; fi' 0 INT
if plymouth --ping 2>/dev/null
then
plymouth change-mode --system-upgrade
plymouth system-update --progress=0
fi
i=0
step=
while read -r -a words
do
# empty line: skip it!
if [[ "${#words[@]}" -eq 0 ]]
then
continue
fi
# the pacman steps:
# :: Synchronizing package databases...
# :: Starting full system upgrade...
# :: Proceed with installation? [Y/n]
# :: Retrieving packages...
# :: Running pre-transaction hooks...
# :: Processing package changes...
# :: Running post-transaction hooks...
if [[ "${words[0]}" =~ :: ]]
then
i=0
step="${words[*]:1}"
if [[ "$step" == "Synchronizing package databases..." ]] ||
[[ "$step" == "Retrieving packages..." ]] ||
[[ "$step" == "Running pre-transaction hooks..." ]] ||
[[ "$step" == "Running post-transaction hooks..." ]]
then
echo "$step"
fi
continue
fi
# nothing to do: exit early!
if [[ "${words[0]}" == "there is nothing to do" ]]
then
echo "Nothing to do, rebooting..."
break
fi
# :: Starting full system upgrade...
# resolving dependencies...
# looking for conflicting packages...
#
# Packages (4) pkg1 pkg2 pkg3 pkg4
#
# Total Download Size: ##.## MiB
# Total Installed Size: ##.## MiB
# Net Upgrade Size: ##.## MiB
#
if [[ "${words[0]}" == "Packages" ]]
then
# Packages (4) (...)
if [[ "${words[1]}" =~ ^\([0-9]+\)$ ]]
then
count="${words[1]:1:$((${#words[1]}-2))}"
fi
# :: Retrieving packages...
# downloading pkg...
# :: Processing package changes...
# upgrading pkg...
elif [[ "${words[0]}" =~ ^(downloading|installing|upgrading)$ ]] &&
[[ "$step" != "Synchronizing package databases..." ]]
then
i="$((i+1))"
processing="${words[0]:0:1}"
processing="${processing^^}"
processing+="${words[0]:1}"
if [[ -z "${count:-}" ]]
then
echo "$processing ${words[1]}"
continue
elif [[ "${words[0]}" == downloading ]]
then
echo "$processing ${words[1]} ($i/$count)"
continue
fi
progress "$((20+(60*i)/count))"
echo "$processing ${words[1]} ($i/$count)"
continue
# :: Running pre-transaction hooks...
# ( #/##) Doing something...
# (##/##) Doing another thing...
# :: Running post-transaction hooks...
# (#/#) Doing a last thing...
elif [[ "$step" == "Running pre-transaction hooks..." ]] ||
[[ "$step" == "Running post-transaction hooks..." ]]
then
# ( #/##) (...): remove the parasite space.
if [[ "${words[0]}" == "(" ]]
then
words[1]="${words[0]}${words[1]}"
words[0]=
words=("${words[@]:1}")
fi
if ! [[ "${words[0]}" =~ ^\([0-9]+/[0-9]+\)$ ]]
then
echo "${words[*]}"
continue
fi
if [[ "$step" == "Running pre-transaction hooks..." ]]
then
base=0
else
base=80
fi
val="${words[0]:1:$((${#words[0]}-2))}"
progress "$((base+20*${val// /}))"
echo "${words[*]:1} ${words[0]}"
continue
fi
done < <(pacman "${opts[@]}" -Su "$@")
rm -f /var/lib/system-update/*.pkg.tar.*
if plymouth --ping 2>/dev/null
then
plymouth change-mode --boot-up
plymouth display-message --text="Rebooting..."
fi
echo "Rebooting..."
sleep 5s

View File

@@ -0,0 +1,46 @@
#!/bin/bash
set -e
die() { echo >&2 "!! $*"; exit 1; }
SENTINEL_FILE="steamos-session-select"
SENTINEL_VALUE="plasma-steamos-oneshot.desktop"
# If we proceed, execute this
CHAINED_SESSION="/usr/bin/startplasma-x11"
# If we decide the sentinel is consumed, execute this command instead and fail
RESTORE_SESSION=(steamos-session-select) # No arguments restores the session
# Find or check config sentinel
check_sentinel()
{
if [[ -z ${HOME+x} ]]; then
echo >&2 "$0: No \$HOME variable!"
# Rather than break we'll just launch plasma and hope for the best?
return 0
fi
local config_dir="${XDG_CONF_DIR:-"$HOME/.config"}"
(
cd "$HOME"
cd "$config_dir"
sentinel_value="$(cat "$SENTINEL_FILE")"
[[ $sentinel_value = "$SENTINEL_VALUE" ]] || return 1
rm "$SENTINEL_FILE"
) || return 1 # If we couldn't read the value or it wasn't what we wanted
# Found value and removed it, we're good to continue
return 0
}
if check_sentinel; then
# We found and consumed the oneshot sentinel, proceed to launch plasma
echo >&2 "$0: Found and removed sentinel file for one-shot plasma, proceeding to launch"
exec "$CHAINED_SESSION"
else
echo >&2 "$0: Sentinel value not found, executing session-select to restore session"
"${RESTORE_SESSION[@]}" || echo >&2 "$0: !! Failed to restore previous session, executing chained session"
# Session restore should've stopped us, if it is broken at least let plasma continue to open
exec "$CHAINED_SESSION"
fi

View File

@@ -0,0 +1,18 @@
#!/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 "$@";

View File

@@ -0,0 +1,177 @@
#!/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
DISK= # --disk=
PARTSET= # --partset=
SYMLINKS_DIR=/dev/disk/by-partsets
[ -e /usr/lib/steamos/steamos-partitions-lib ] && \
. /usr/lib/steamos/steamos-partitions-lib || \
{ echo "Failed to source '/usr/lib/steamos/steamos-partitions-lib'"; exit 1; }
# Helpers
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] --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 "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
;;
--)
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.
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
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
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 "$@"

View File

@@ -0,0 +1 @@
steamos-logger

View File

@@ -0,0 +1 @@
steamos-logger

View File

@@ -0,0 +1 @@
steamos-logger

View File

@@ -0,0 +1 @@
steamos-logger

View File

@@ -0,0 +1,90 @@
#!/bin/bash
# -*- mode: sh; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# vim: et sts=4 sw=4
# SPDX-License-Identifier: LGPL-2.1+
#
# Copyright © 2019-2021 Collabora Ltd.
# Copyright © 2019-2021 Valve Corporation.
#
# This file is part of steamos-customizations.
#
# steamos-customizations is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation; either version 2.1 of the License,
# or (at your option) any later version.
set -e
set -u
FORCE=0 # --force
FACTORY_RESET_CONFIG_DIR=/esp/efi/steamos/factory-reset
[ -e /usr/lib/steamos/steamos-partitions-lib ] && \
. /usr/lib/steamos/steamos-partitions-lib || \
{ echo "Failed to source '/usr/lib/steamos/steamos-partitions-lib'"; exit 1; }
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 in:"
echo " $FACTORY_RESET_CONFIG_DIR"
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 "$FACTORY_RESET_CONFIG_DIR"; then
reboot
fi

View File

@@ -0,0 +1,43 @@
#!/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 © 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 -eu
SYMLINKS_DIR=/dev/disk/by-partsets
FACTORY_RESET_CONFIG_DIR=/esp/efi/steamos/factory-reset
HOME_DEVICE=$SYMLINKS_DIR/all/home
ESP_DEVICE=$SYMLINKS_DIR/all/esp
cdir=${1:-$FACTORY_RESET_CONFIG_DIR}
wdir=$(mktemp -t -d rest-conf-XXXXXXXXXX)
dev=
opts=
for dev in $SYMLINKS_DIR/all/efi-*; do
echo EFI $dev $(readlink -f $dev) > ${wdir}/${dev##*/}.cfg
done
for dev in $SYMLINKS_DIR/all/var-*; do
opts=$(tune2fs -l "$dev" | sed -n 's/^Filesystem features:\s*//p')
echo VAR $dev $(readlink -f $dev) "$opts" > ${wdir}/${dev##*/}.cfg
done
opts=$(tune2fs -l "$HOME_DEVICE" | sed -n 's/^Filesystem features:\s*//p')
echo HOME $HOME_DEVICE $(readlink -f $HOME_DEVICE) "$opts" > ${wdir}/home.cfg
mkdir -p "$cdir"
mv "${wdir}"/* "$cdir"
rmdir "$wdir"

View File

@@ -0,0 +1 @@
steamos-logger

View File

@@ -0,0 +1,195 @@
#!/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 -e
set -o pipefail
set -u
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
mapfile -t moduledirs < <(ls -1d /usr/lib/steamos/modules/* 2>/dev/null)
for moduledir in "${moduledirs[@]}"
do
if ! read -r pkgbase > /dev/null 2>&1 < "$moduledir/pkgbase"
then
# if the kernel has no pkgbase, we skip it
continue
fi
kver="${moduledir##*/}"
install -Dm644 "/usr/share/factory/var/lib/modules/$kver/vmlinuz" "/boot/vmlinuz-$pkgbase"
mkdir -p "/var/lib/modules/$kver"
cp -a "/usr/share/factory/var/lib/modules/$kver" "/var/lib/modules/"
dracut --force --hostonly "/boot/initramfs-$pkgbase.img" "$kver"
dracut --force --no-hostonly "/boot/initramfs-$pkgbase-fallback.img" "$kver"
done
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:
find /var/lib/overlays/etc/upper/NetworkManager/system-connections/ -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/*
}
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
else
echo "Skipping configuration migration steps as this is not running in a chroot."
fi
fi
if [[ ! ${no_boot_install:-} ]]
then
install_bootloaders
fi

View File

@@ -0,0 +1 @@
steamos-reboot

View File

@@ -0,0 +1 @@
steamos-logger

View File

@@ -0,0 +1,132 @@
#!/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 -e
set -u
usage() {
cat <<EOF
Usage: ${0##*/} [options] [<message>]
Echoes the message to standard output.
It displays the text message if splash screen is running.
The splash screen is cleared if message is empty.
Options:
-h, --help this help message
-l, --log-level LEVEL the level of logs
LEVELS
Valid levels: emergency alert critical error warning notice info debug.
EOF
}
declare -A LOG_LEVEL
LOG_LEVEL[emergency]="0"
LOG_LEVEL[fatal]="1"
LOG_LEVEL[critical]="2"
LOG_LEVEL[error]="3"
LOG_LEVEL[warning]="4"
LOG_LEVEL[notice]="5"
LOG_LEVEL[info]="6"
LOG_LEVEL[debug]="7"
declare -A LOG_LEVEL_PREFIX
LOG_LEVEL_PREFIX[emergency]="<0>"
LOG_LEVEL_PREFIX[fatal]="<1>"
LOG_LEVEL_PREFIX[critical]="<2>"
LOG_LEVEL_PREFIX[error]="<3>"
LOG_LEVEL_PREFIX[warning]="<4>"
LOG_LEVEL_PREFIX[notice]="<5>"
LOG_LEVEL_PREFIX[info]="<6>"
LOG_LEVEL_PREFIX[debug]="<7>"
declare -A PLYMOUTH_PREFIX
PLYMOUTH_PREFIX[emergency]="Emergency: "
PLYMOUTH_PREFIX[fatal]="Fatal: "
PLYMOUTH_PREFIX[critical]="Critical: "
PLYMOUTH_PREFIX[error]="Error: "
PLYMOUTH_PREFIX[warning]="Warning: "
if [[ ${STEAMOS_DEBUG:-} ]]
then
PLYMOUTH_PREFIX[notice]="Notice: "
PLYMOUTH_PREFIX[info]="Info: "
PLYMOUTH_PREFIX[debug]="Debug: "
fi
if [[ "${0##*/}" != steamos-logger ]]
then
log_level="${0##*/}"
log_level="${log_level#steamos-}"
else
log_level="notice"
fi
while [[ "$#" -ne 0 ]]
do
if [[ "$1" =~ ^(-h|--help) ]]
then
usage
exit
elif [[ "$1" =~ ^(-l|--log-level) ]]
then
shift
if [[ "$#" -eq 0 ]] || [[ -z ${LOG_LEVEL_PREFIX[$1]:-} ]]
then
echo "${!LOG_LEVEL_PREFIX[@]}"
if [[ "$#" -eq 0 ]]
then
exit 0
fi
exit 1
fi
log_level="$1"
else
break
fi
shift
done
if plymouth --ping &&
{ [[ ${STEAMOS_DEBUG:-} ]] ||
[[ "${LOG_LEVEL[$log_level]}" -lt "${LOG_LEVEL[info]}" ]]; }
then
prefix="${PLYMOUTH_PREFIX[$log_level]:-}"
if [[ ${prefix:-} ]]
then
text="$prefix"
fi
text+="${*:-}"
plymouth display-message --text="$text"
unset prefix text
fi
if [[ "$#" -eq 0 ]]
then
exit
fi
if ! [[ -t 1 ]]
then
prefix="${LOG_LEVEL_PREFIX[$log_level]:-}"
fi
exec printf "%s%s\n" "${prefix:-}" "${*:-}"

View File

@@ -0,0 +1,150 @@
#!/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 © 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 -e
set -u
declare -r esp_parttype=c12a7328-f81f-11d2-ba4b-00a0c93ec93b
declare -r efi_parttype=ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
declare -r lsblk_cols=PATH,PARTTYPE,PARTUUID,PARTLABEL
MOUNTDIR="${MOUNTDIR:-/mnt}"
OUTPUTS=("path" "partuuid" "partlabel" "label" "boot-requested-at" "boot-other"
"boot-other-disabled" "boot-attempts" "boot-count" "boot-time"
"image-invalid" "update" "update-disabled" "update-window-start"
"update-window-end" "loader" "partitions" "comment")
usage() {
cat <<EOF
${0##*/} [options] [<device> ...]
List bootconf parameters about SteamOS devices.
Options:
-o, --output <list> output columns
EOF
}
opts=()
while [[ "$#" -ne 0 ]]
do
if [[ "$1" =~ ^(-h|--help)$ ]]
then
usage
exit 0
elif [[ "$1" =~ ^(-o|--output)$ ]]
then
shift
if [[ "$1" == "help" ]]
then
echo "${OUTPUTS[@]}"
exit 0
fi
IFS=, read -r -a output <<<"$1"
elif [[ "$1" == -- ]]
then
shift
break
else
opts+=("$1")
fi
shift
done
esp_path=
mapfile -t espdev < <(lsblk --noheadings --output $lsblk_cols)
for dev in "${espdev[@]}"
do
read -r path parttype partuuid partlabel <<<"$dev"
[[ "${parttype:-}" == $esp_parttype ]] || continue
# We need this check in case there are other OSes installed with
# esp_parttype tagged partitions of their own:
[[ "${partlabel:-}" == esp ]] || continue
esp_path=$path
break
done
mapfile -t devices < <(lsblk --noheadings --output $lsblk_cols "${opts[@]}" "$@")
for dev in "${devices[@]}"
do
read -r path parttype partuuid partlabel <<<"$dev"
[[ "${parttype:-}" == $efi_parttype ]] || continue
image_ident=
case $partlabel in
efi-*)
image_ident=${partlabel#efi-};
;;
esac
[[ -n "${image_ident}" ]] || continue;
if ! mount -oro "$esp_path" ${MOUNTDIR}
then
echo "Warning: $path: No such mountable device" >&2
continue
fi
trap "umount ${MOUNTDIR}" 0
cfgs=()
if [[ -e "$MOUNTDIR/SteamOS/conf/${image_ident}.conf" ]]
then
mapfile -t cfgs <"$MOUNTDIR/SteamOS/conf/${image_ident}.conf"
fi
# FIXME: Small delay to avoid umount exiting with EBUSY
while ! umount "$MOUNTDIR"
do
sleep 0.01
done 2>/dev/null
trap - 0
[[ "${#cfgs[@]}" -ne 0 ]] || continue
if [[ ! "${output[@]:-}" ]]
then
printf "$path@/SteamOS/bootconf: %s\n" "${cfgs[@]}"
continue
fi
vals=()
cfgs+=("path: $path")
cfgs+=("partuuid: $partuuid")
cfgs+=("partlabel: $partlabel")
for i in "${output[@]}"
do
val=
for cfg in "${cfgs[@]}"
do
read -r p v <<<"$cfg"
p="${p%:}"
[[ "$i" == "$p" ]] || continue
val="$v"
break
done
if [[ ! "$val" ]] && [[ "${FS:- }" == " " ]]
then
val="-"
fi
vals+=("$val")
done
( IFS="${FS:- }"; printf "%s" "${vals[*]}"; printf "\n" )
done

View File

@@ -0,0 +1,258 @@
#!/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 -e
set -o pipefail
set -u
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

View File

@@ -0,0 +1 @@
steamos-logger

View File

@@ -0,0 +1,204 @@
#!/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
OUTDIR= # $1
DEVICES= # --devices=
[ -e /usr/lib/steamos/steamos-partitions-lib ] && \
. /usr/lib/steamos/steamos-partitions-lib || \
{ echo "Failed to source '/usr/lib/steamos/steamos-partitions-lib'"; exit 1; }
# Helpers
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)

View File

@@ -0,0 +1 @@
steamos-reboot

View File

@@ -0,0 +1,145 @@
#!/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
# default and future guess
fstype=btrfs
usage() {
cat <<EOF
Usage: ${0##*/} enable|disable|status
Enable or disable read-only on the current running SteamOS.
EOF
}
# mark root partition writable
read_write_extfs() {
tune2fs -O ^read-only "$ROOTDEV"
mount -o remount,rw /
}
read_write_btrfs() {
mount -o remount,rw /
btrfs property set / ro false
}
read_write() {
if ! status >/dev/null; then
echo "Warning: The rootfs is already read-write!" >&2
echo " Nothing is performed." >&2
return
fi
read_write_$fstype
}
#
# mark root partition read-only
read_only_btrfs() {
btrfs property set / ro true
}
read_only_extfs() {
mount -o remount,ro /
tune2fs -O read-only "$ROOTDEV"
}
read_only() {
if status >/dev/null; then
echo "Warning: The rootfs is already read-only!" >&2
echo " Nothing is performed." >&2
return
fi
sync /
read_only_$fstype
}
status_extfs() {
if tune2fs -l "$ROOTDEV" | grep -q '^Filesystem features: .*read-only.*$'
then
echo "enabled"
return
else
echo "disabled"
return 1
fi
}
status_btrfs() {
prop_val=$(btrfs property get / ro)
if [[ $prop_val = "ro=true" ]]
then
echo "enabled"
else
echo "disabled"
return 1
fi
}
status() {
status_$fstype
}
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
ext4)
fstype=extfs
;;
btrfs)
fstype=btrfs
;;
*)
echo "Unrecognized root filesystem type $FSTYPE"
exit 1
esac
}
# Ideally status will be root-free, alas tune2fs (ext4 rootfs)
# does not like that.
if [[ "$(id -u)" -ne 0 ]]; then
echo "$(basename $0) needs to be run as root"
exit 1
fi
get_fstype
case "${1:-}" in
disable)
read_write
;;
enable)
read_only
;;
toggle)
toggle
;;
status)
status
;;
*)
usage
exit 1
esac

View File

@@ -0,0 +1,216 @@
#!/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
--update
Set mode update to update at next boot
--update-other
Set mode update-other to update other at next boot
--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 /esp/efi/steamos/factory-reset
/usr/bin/steamos-set-bootmode reboot
elif [[ "$1" =~ ^--update$ ]] || [[ "$1" =~ ^--(update|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[@]}"

View File

@@ -0,0 +1,36 @@
#!/bin/bash
set -e
BRANCH_PATH="/var/lib/steamos-branch"
if [[ "$#" = 1 ]]; then
case "$1" in
"-c")
branch=$(cat "$BRANCH_PATH" 2> /dev/null || echo "rel")
case "$branch" in
"rel" | "rc" | "beta" | "bc" | "main")
echo "$branch"
exit 0
;;
*)
echo "unknown branch name in $BRANCH_PATH: $branch" 1>&2
exit 1
;;
esac
;;
"-l")
echo rel
echo rc
echo beta
echo bc
echo main
exit 0
;;
"rel" | "rc" | "beta" | "bc" | "main")
echo "$1" > "$BRANCH_PATH"
exit 0
;;
esac
fi
echo "Usage: steamos-select-branch <-c|-l|rel|rc|beta|bc|main>" 1>&2

View File

@@ -0,0 +1,84 @@
#!/bin/bash
set -e
die() { echo >&2 "!! $*"; exit 1; }
# FIXME Purge old files and use this one
# File this script will modify, in addition to (potentially) the per-user sentinel file
CONF_FILE="/etc/sddm.conf.d/zz-steamos-autologin.conf"
SENTINEL_FILE="steamos-session-select"
# 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}"
session_launcher=""
create_sentinel=""
session_uses_x11=""
case "$session" in
plasma-wayland-persistent)
session_launcher="plasmawayland.desktop"
;;
plasma-persistent)
session_launcher="plasma.desktop"
session_uses_x11=1
;;
plasma)
session_launcher="plasma-steamos-oneshot.desktop"
create_sentinel=1
session_uses_x11=1
;;
gamescope)
session_launcher="gamescope-wayland.desktop"
;;
*)
echo >&2 "!! Unrecognized session '$session'"
exit 1
;;
esac
if [[ "$2" == "--sentinel-created" ]]; then
SENTINEL_CREATED=1
fi
# Update config sentinel
if [[ -z $SENTINEL_CREATED ]]; then
[[ -n ${HOME+x} ]] || die "No \$HOME variable"
config_dir="${XDG_CONF_DIR:-"$HOME/.config"}"
(
cd "$HOME"
mkdir -p "$config_dir"
cd "$config_dir"
echo "$session_launcher" > "$SENTINEL_FILE"
)
# If we were executed as a session user and then re-execute as root below, we don't want to set root's sentinel too
export SENTINEL_CREATED=1
echo "Updated user selected session to $session_launcher"
fi
# Become root
if [[ $EUID != 0 ]]; then
exec pkexec "$(realpath $0)" "$session" --sentinel-created
exit 1
fi
{
if [[ -n $session_uses_x11 ]]; then
# Default is Wayland
echo "[General]"
echo "DisplayServer=X11"
fi
echo "[Autologin]"
echo "Session=$session_launcher"
} > "$CONF_FILE"
echo "Updated system autologin session to $session_launcher"
systemctl reset-failed sddm
systemctl restart sddm
echo "Restarted SDDM"

View File

@@ -0,0 +1,113 @@
#!/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

View File

@@ -0,0 +1,170 @@
#!/bin/bash
set -eu
##
## This script must match the API the temporary Steam UI updater wants of us, including this file
##
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 "$*"; }
die() { info "!! $*"; exit 1; }
checkmode=""
error=""
beta=""
debug=""
while [[ $# -ge 1 ]]; do
case "$1" in
"check") checkmode=1 ;;
"--beta") beta=1 ;;
"-d") debug=1 ;;
*)
error=1
info "Unknown option \"$1\""
;;
esac
shift
done
if [[ -n $error ]]; then
echo >&2 "!! Usage: $0 [check]"
exit 1
fi
atomupd_args=(--manifest "/usr/share/steamos-update/manifest-0.json")
[[ -n $debug ]] && atomupd_args+=(-d)
# Determine which branches to check.
check_rel=0
check_rc=0
check_beta=0
check_bc=0
check_main=0
if [[ -n $beta ]]; then
info "'--beta' is deprecated; use 'steamos-select-branch beta' then 'steamos-update'"
check_beta=1
else
branch_path="/var/lib/steamos-branch"
branch=$(cat "$branch_path" 2> /dev/null || echo "rel")
case "$branch" in
"rel")
check_rel=1
;;
"rc")
check_rel=1
check_rc=1
;;
"beta")
check_beta=1
;;
"bc")
check_beta=1
check_bc=1
;;
"main")
check_main=1
;;
*)
echo "unknown branch name in $branch_path: $branch" 1>&2
exit 1
;;
esac
fi
# Get the buildid for each variant.
get_buildid () {
if [[ $1 = 0 ]]; then
exit 0
fi
query="$(sudo steamos-atomupd-client "${atomupd_args[@]}" --variant="$2" --query-only)"
if [[ -z $query ]]; then
info "Failed to check for updates"
exit 1 # Unknown failure
fi
jq -r '.minor.candidates[0].image.buildid | select(type == "string")' <<< "$query"
}
buildid_rel="$(get_buildid $check_rel steamdeck)"
buildid_rc="$(get_buildid $check_rc steamdeck-rc)"
buildid_beta="$(get_buildid $check_beta steamdeck-beta)"
buildid_bc="$(get_buildid $check_bc steamdeck-bc)"
buildid_main="$(get_buildid $check_main steamdeck-main)"
if [[ -n $debug ]]; then
info "buildid_rel=$buildid_rel"
info "buildid_rc=$buildid_rc"
info "buildid_beta=$buildid_beta"
info "buildid_bc=$buildid_bc"
info "buildid_main=$buildid_main"
fi
# Choose the variant with the newest buildid.
chosen_buildid=""
chosen_variant=""
if [[ "$buildid_rel" > "$chosen_buildid" ]]; then
chosen_buildid="$buildid_rel"
chosen_variant="steamdeck"
fi
if [[ "$buildid_rc" > "$chosen_buildid" ]]; then
chosen_buildid="$buildid_rc"
chosen_variant="steamdeck-rc"
fi
if [[ "$buildid_beta" > "$chosen_buildid" ]]; then
chosen_buildid="$buildid_beta"
chosen_variant="steamdeck-beta"
fi
if [[ "$buildid_bc" > "$chosen_buildid" ]]; then
chosen_buildid="$buildid_bc"
chosen_variant="steamdeck-bc"
fi
if [[ "$buildid_main" > "$chosen_buildid" ]]; then
chosen_buildid="$buildid_main"
chosen_variant="steamdeck-main"
fi
if [[ -z "$chosen_variant" ]]; then
info "No update available"
exit 7
fi
manifest_path="/etc/steamos-atomupd/manifest.json"
current_buildid=$(jq -r .buildid < "$manifest_path")
if [[ "$current_buildid" = "$chosen_buildid" ]]; then
info "No update available"
exit 7
fi
if [[ -n $debug ]]; then
info "chosen_buildid=$chosen_buildid"
info "chosen_variant=$chosen_variant"
fi
# Update is available
info "Update available"
# Check mode, return success for update available
if [[ -n ${checkmode-} ]]; then
echo "$chosen_buildid"
exit 0
fi
# Not check mode. Update!
do_atomupd() { sudo steamos-atomupd-client "${atomupd_args[@]}" --variant="$chosen_variant" "$@"; }
if do_atomupd; then
info "Applied an update"
exit 0
else
info "Update failed"
exit 1
fi

View File

@@ -0,0 +1,69 @@
#!/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##*/} after-reboot
${0##*/} -h|--help
EOF
}
update_now() {
if ! steamos-atomupd-client "$@"; then
echo "SteamOS cannot be updated!" >&2
sleep 5s
fi
if grep -q 'systemd.unit=steamos-update-os.target' /proc/cmdline; then
steamos-logger "Rebooting in 5 seconds...";
sleep 5s
reboot
else
echo "Reboot to run the new version of SteamOS." >&2
fi
}
update_after_reboot() {
/usr/sbin/steamos-set-bootmode update-other
echo "Reboot to start the update of SteamOS." >&2
}
if [[ $# -eq 0 ]]; then
usage
exit 1
fi
case "$1" in
(now)
shift
update_now "$@"
;;
(after-reboot)
shift
update_after_reboot
;;
(-h|--help)
usage
exit 0
(*)
usage
exit 1
;;
esac

View File

@@ -0,0 +1,144 @@
#!/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

View File

@@ -0,0 +1 @@
steamos-logger