agama/live/live-root/usr/bin/live-self-update
Ladislav Slezák eb76ca8139
Fixed starting the nmtui and self-update services (#3504)
## 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
2026-05-15 17:08:24 +02:00

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