## Problem - The self-update service is not started at boot ## Details - The service files have been moved from `/usr/lib/systemd/system` to `/etc/systemd/system` - The dracut complains: ``` dracut[I]: *** Including module: live-self-update *** dracut-install: ERROR: installing '/usr/lib/systemd/system/live-self-update.service' dracut[E]: FAILED: /usr/lib/dracut/dracut-install -D /var/tmp/dracut.AltRpF/initramfs -a /usr/lib/systemd/system/live-self-update.service live-self-update Failed to enable unit: Unit live-self-update.service does not exist ``` - Unfortunately dracut continues and ignores the error, that's the reason nobody noticed the problem ## Solution - Fix the service file location - The same problem was found in the `initrd-nmtui.service` file - Use `set -e` to avoid similar problems in the future - Return the original exit value so the `systemctl status live-self-update.service` does not report a failure ## Testing - Tested with a locally built ISO, the self-update service starts properly
330 lines
11 KiB
Bash
Executable file
330 lines
11 KiB
Bash
Executable file
#! /bin/bash
|
|
|
|
shopt -s nullglob
|
|
|
|
[ -e /dracut-state.sh ] && . /dracut-state.sh
|
|
|
|
# Later we could disable "-x" or enable it on request, let's be more verbose in
|
|
# the initial implementation. Since we have enabled using the plymouth splash
|
|
# screen the verbose details will not be visible to users by default.
|
|
set -ex
|
|
|
|
. "$(dirname "$0")"/../lib/live-self-update/conf.sh
|
|
|
|
RESPONSE_FILE="$RUN_DIR/server_response"
|
|
REG_REPO_FILE="$RUN_DIR/reg_repos"
|
|
OSR="/etc/os-release"
|
|
ARCH=$(uname -m)
|
|
|
|
# do the cleanup at the end, trap ensures it runs even if something fails
|
|
trap cleanup_chroot EXIT
|
|
|
|
# helper function to log important steps (highlighted in the journalctl output)
|
|
# use "set +x" to avoid logging the message again (it is already logged in the
|
|
# original caller place so it would be actually logged three times...)
|
|
# see https://www.freedesktop.org/software/systemd/man/latest/sd-daemon.html
|
|
# for the log level numbers
|
|
milestone() {
|
|
({ set +x; } 2>/dev/null; echo "<5>$1")
|
|
}
|
|
|
|
# log warning
|
|
warning() {
|
|
({ set +x; } 2>/dev/null; echo "<4>$1")
|
|
}
|
|
|
|
# log error
|
|
error() {
|
|
({ set +x; } 2>/dev/null; echo "<3>$1")
|
|
}
|
|
|
|
# normal message
|
|
info() {
|
|
({ set +x; } 2>/dev/null; echo "<6>$1")
|
|
}
|
|
|
|
# the proxy might contain a password, make sure it is not displayed or logged
|
|
# when processing it
|
|
set +x
|
|
# get the configured network proxy (empty string if not configured)
|
|
# see ../lib/dracut/modules.d/98dracut-menu/dracut-cmdline-menu.sh
|
|
# get the last found, it has the highest priority
|
|
PROXY=$(grep "\bproxy=" /proc/cmdline /etc/cmdline.d/* | tail -n 1 | sed "s/.*\bproxy=\([^ \t]*\).*/\1/")
|
|
|
|
if [ -n "$PROXY" ]; then
|
|
if [[ "$PROXY" == *@* ]]; then
|
|
# looks like there is a username or password in the URL, do not print it
|
|
milestone "Using authenticated HTTP proxy"
|
|
else
|
|
milestone "Using proxy server $PROXY"
|
|
fi
|
|
|
|
# set the proxy for all zypper and curl calls, for security reasons curl
|
|
# uses only the lower case name, zypper accepts that as well,
|
|
# see https://everything.curl.dev/usingcurl/proxies/env.html
|
|
export http_proxy="$PROXY"
|
|
export https_proxy="$PROXY"
|
|
|
|
# hide the original URL to avoid leaking the password in URL later
|
|
PROXY=1
|
|
fi
|
|
set -x
|
|
|
|
# helper for running a command in the live root system, works both when running
|
|
# in initramfs ($NEWROOT is set) and in already mounted root ($NEWROOT unset)
|
|
run_command() {
|
|
if [ -z "$NEWROOT" ]; then
|
|
"$@"
|
|
else
|
|
"$NEWROOT/usr/bin/chroot" "$NEWROOT" "$@"
|
|
fi
|
|
}
|
|
|
|
# Prepare the system root to be used by scripts before switching to it.
|
|
prepare_chroot() {
|
|
if [ -n "$NEWROOT" ]; then
|
|
milestone "Preparing for the self update..."
|
|
|
|
# make system directories active also in the chroot directory
|
|
mount -o bind /run "$NEWROOT"/run
|
|
mount -o bind /sys "$NEWROOT"/sys
|
|
mount -o bind /proc "$NEWROOT"/proc
|
|
mount -o bind /dev "$NEWROOT"/dev
|
|
[ -f /run/NetworkManager/resolv.conf ] && cp /run/NetworkManager/resolv.conf "$NEWROOT"/etc/resolv.conf
|
|
fi
|
|
# remove any existing self-update repositories, probably left-overs from the previous run
|
|
rm -f "$NEWROOT"/etc/zypp/repos.d/self-update-*.repo
|
|
}
|
|
|
|
# Revert the changes done by prepare_chroot() and restore the system to the
|
|
# original state, called from bash "trap" handler
|
|
cleanup_chroot() {
|
|
local result="$?"
|
|
|
|
if [ "$result" != "0" ]; then
|
|
error "Error: Self update failed! (exit status: $result)"
|
|
else
|
|
milestone "Self update successfully finished"
|
|
fi
|
|
|
|
# log the result to be checked by openQA or Agama itself
|
|
echo "$result" > "$RESULT_FILE"
|
|
|
|
if [ -n "$NEWROOT" ]; then
|
|
rm -f "$NEWROOT"/etc/resolv.conf
|
|
# do not fail if not mounted
|
|
findmnt "$NEWROOT"/run > /dev/null && umount "$NEWROOT"/run
|
|
findmnt "$NEWROOT"/sys > /dev/null && umount "$NEWROOT"/sys
|
|
findmnt "$NEWROOT"/proc > /dev/null && umount "$NEWROOT"/proc
|
|
findmnt "$NEWROOT"/dev > /dev/null && umount "$NEWROOT"/dev
|
|
fi
|
|
|
|
# return the original exit value
|
|
exit "$result"
|
|
}
|
|
|
|
# read the data from /etc/os-release file (source it)
|
|
read_os_release() {
|
|
# get the product data
|
|
if [ -f "$OSR" ]; then
|
|
. "$OSR"
|
|
else
|
|
error "File $OSR is missing!"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# ask the registration server (SCC or RMT) for the self-update URL
|
|
query_reg_server() {
|
|
local server
|
|
# custom server (RMT)
|
|
if [ -f "$SERVER_FILE" ]; then
|
|
server=$(< "$SERVER_FILE")
|
|
# default server (SCC)
|
|
elif [ -f "$CONFIG_DEFAULT_REG_SERVER_FILE" ]; then
|
|
server=$(< "$CONFIG_DEFAULT_REG_SERVER_FILE")
|
|
fi
|
|
|
|
if [ -n "$server" ]; then
|
|
read_os_release
|
|
|
|
local query
|
|
query="$server/connect/repositories/installer?identifier=$NAME&version=$VERSION_ID&arch=$ARCH"
|
|
info "Running query $query...."
|
|
|
|
local curl_options
|
|
curl_options=(--silent --globoff --location --retry 3 --retry-connrefused \
|
|
--connect-timeout 20 --max-time 10 --fail --show-error --output "$RESPONSE_FILE")
|
|
|
|
if grep -q "\binst.self_update_ssl=0\b" /run/agama/cmdline.d/agama.conf; then
|
|
warning "Disabling SSL check for $server"
|
|
curl_options+=("--insecure")
|
|
fi
|
|
|
|
if [ -n "$PROXY" ]; then
|
|
# automatically detect the proxy authentication method (basic/digest)
|
|
curl_options+=("--proxy-anyauth")
|
|
fi
|
|
|
|
if curl "${curl_options[@]}" -- "$query" && [ -s "$RESPONSE_FILE" ]; then
|
|
info "Found self-update repository: $(jq -r ".[] | select(.installer_updates==true) | .description" "$RESPONSE_FILE") "
|
|
jq -r ".[] | select(.installer_updates==true) | .url" "$RESPONSE_FILE" > "$REG_REPO_FILE"
|
|
else
|
|
warning "Contacting the registration server failed"
|
|
# SCC/RMT query failed and no fallback configured
|
|
if [ ! -s "$CONFIG_FALLBACK_FILE" ]; then
|
|
error "Cannot obtain the self-update repository"
|
|
exit 1
|
|
fi
|
|
fi
|
|
else
|
|
milestone "Registration server not defined"
|
|
fi
|
|
}
|
|
|
|
# add the self-update repositories into the system
|
|
add_update_repos() {
|
|
milestone "Adding self update repositories..."
|
|
# add the self-update repositories
|
|
while read -r update_url; do
|
|
if [ -n "$update_url" ]; then
|
|
local repo
|
|
repo="self-update-$index"
|
|
|
|
if grep -q "\binst.self_update_ssl=0\b" /run/agama/cmdline.d/agama.conf; then
|
|
warning "Disabling SSL check for repository $update_url"
|
|
# add "ssl_verify=no" query to the repository URL
|
|
update_url=$(url_add_query "$update_url" "ssl_verify=no")
|
|
fi
|
|
|
|
local zypper_options
|
|
zypper_options=()
|
|
# allow using unsigned repository
|
|
if grep -q "\binst.self_update_unsigned_repo=1\b" /run/agama/cmdline.d/agama.conf; then
|
|
warning "Unsigned repository is allowed"
|
|
zypper_options+=("--gpgcheck-allow-unsigned-repo")
|
|
fi
|
|
|
|
# completely disable GPG checks, not recommended, if possible the options
|
|
# above should be preferred
|
|
if grep -q "\binst.self_update_gpg=0\b" /run/agama/cmdline.d/agama.conf; then
|
|
warning "Disabling all GPG checks!"
|
|
zypper_options+=("--no-gpgcheck")
|
|
fi
|
|
|
|
run_command zypper addrepo "${zypper_options[@]}" -f "$update_url" "$repo"
|
|
self_update_repos+=("$repo")
|
|
((++index))
|
|
fi
|
|
done < "$1"
|
|
}
|
|
|
|
# Reads the configured fallback repository, the URL might contain several
|
|
# placeholders. This way, it works across multiple architectures and does not
|
|
# contain a hardcoded version (the fallback URL should work with SLES-16.0,
|
|
# SLES-16.1, SLES-16.2...)
|
|
read_fallback_repos() {
|
|
# Substituting $arch pattern with the architecture of the current system.
|
|
# Substituting these variables with the /etc/os-release content:
|
|
# $os_release_name => NAME
|
|
# $os_release_id => ID
|
|
# $os_release_version => VERSION
|
|
# $os_release_version_id => VERSION_ID
|
|
# IMPORTANT NOTE: the "VERSION_ID" replacement must happen *before*
|
|
# the "VERSION" replacement because it partially matches that string!
|
|
sed -e "s/\$os_release_name/$NAME/g" -e "s/\$os_release_id/$ID/g" \
|
|
-e "s/\$os_release_version_id/$VERSION_ID/g" \
|
|
-e "s/\$os_release_version/$VERSION/g" \
|
|
-e "s/\$arch/$ARCH/g" "$CONFIG_FALLBACK_FILE" > "$FALLBACK_REPOS_FILE"
|
|
}
|
|
|
|
# Applies all the updates
|
|
#
|
|
# Reads the URL of the updates from $AGAMA_DUD_INFO and process each one.
|
|
install_updates() {
|
|
index=0
|
|
self_update_repos=()
|
|
|
|
if [ -f "$REPO_FILE" ]; then
|
|
prepare_chroot
|
|
add_update_repos "$REPO_FILE"
|
|
else
|
|
# try SCC/RMT
|
|
query_reg_server
|
|
|
|
# repo from the registration server
|
|
if [ -f "$REG_REPO_FILE" ]; then
|
|
if [ -s "$REG_REPO_FILE" ]; then
|
|
prepare_chroot
|
|
add_update_repos "$REG_REPO_FILE"
|
|
else
|
|
milestone "No repository found in the registration server, skipping self-update"
|
|
exit 0
|
|
fi
|
|
# use a fallback URL
|
|
elif [ -s "$CONFIG_FALLBACK_FILE" ]; then
|
|
milestone "Using fallback repository"
|
|
read_fallback_repos
|
|
prepare_chroot
|
|
add_update_repos "$FALLBACK_REPOS_FILE"
|
|
# no self-update repo found, exit (in the openSUSE installer)
|
|
else
|
|
milestone "No self-update repository configured, skipping self-update"
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
local zypper_options
|
|
# automatically import the repository GPG key
|
|
if grep -q "\binst.self_update_import_key=1\b" /run/agama/cmdline.d/agama.conf; then
|
|
warning "Enabled GPG key autoimport"
|
|
zypper_options="--gpg-auto-import-keys"
|
|
fi
|
|
|
|
milestone "Loading repositories..."
|
|
run_command zypper $zypper_options refresh "${self_update_repos[@]}"
|
|
|
|
# build the list of repository options passed to zypper
|
|
repo_options=()
|
|
for repo in "${self_update_repos[@]}"; do
|
|
repo_options+=("--repo" "$repo")
|
|
done
|
|
|
|
milestone "Updating packages..."
|
|
# update the system only using the predefined self-update repositories,
|
|
# allow downgrading and changing vendor (can be useful for testing)
|
|
run_command zypper --non-interactive update --allow-downgrade --allow-vendor-change --details --no-recommends "${repo_options[@]}"
|
|
|
|
milestone "Cleaning the system..."
|
|
# delete the repository caches, not needed anymore, saves space in RAM disk
|
|
run_command zypper clean --all
|
|
|
|
# disable the self update repositories (keep them for debugging purposes)
|
|
for repo in "${self_update_repos[@]}"; do
|
|
run_command zypper modifyrepo --disable "$repo"
|
|
done
|
|
}
|
|
|
|
# add a query parameter to URL
|
|
# $1 - base URL ("http://example.com")
|
|
# $2 - the query part ("foo=bar")
|
|
# returns "http://example.com?foo=bar"
|
|
url_add_query() {
|
|
local base
|
|
local query
|
|
|
|
# split the string at the "?" separator
|
|
query="${1#*\?}"
|
|
base="${1%\?*}"
|
|
|
|
# if the "?" separator is missing then "$query" equals to "$base"
|
|
if [ -z "$query" ] || [ "$query" = "$base" ]; then
|
|
# no query present, add a query part to the URL
|
|
echo "${base}?${2}"
|
|
else
|
|
# a query is already present, append the new part
|
|
echo "${base}?${query}&${2}"
|
|
fi
|
|
}
|
|
|
|
install_updates
|