From abb148916593de63554ef58c53cb1dd8fbbb0ab1 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 25 Jan 2024 15:47:09 +0100 Subject: Try to use more systemd services --- build-initramfs.sh | 6 +- .../source/usr/lib/systemd/system/example.service | 1 - .../system/multi-user.target.wants/example.service | 1 - modules.d/busybox/install-busybox-stage4.sh | 0 modules.d/busybox/install-busybox.sh | 4 +- modules.d/busybox/module-setup.sh | 0 modules.d/conf-tgz/hooks/fetch-config-tgz.sh | 27 - modules.d/conf-tgz/hooks/s3-fetch-config-tgz.sh | 27 + modules.d/conf-tgz/hooks/s3-fetch-config.sh | 63 ++ modules.d/conf-tgz/hooks/s3-unpack-config-tgz.sh | 58 ++ modules.d/conf-tgz/hooks/unpack-config-tgz.sh | 58 -- modules.d/conf-tgz/module-setup.sh | 26 +- .../conf-tgz/services/s3-fetch-config-tgz.service | 13 + .../conf-tgz/services/s3-fetch-config.service | 13 + .../conf-tgz/services/s3-unpack-config-tgz.service | 12 + .../configuration/systemd-newroot.conf | 2 + .../hooks/copy-dnbd3-files-into-newroot.sh | 21 - .../copy-dracut-systemd-files-into-newroot.sh | 33 +- .../copy-openslx-configuration-into-newroot.sh | 41 -- modules.d/dnbd3-rootfs/hooks/fetch-config.sh | 63 -- .../hooks/load-custom-kernel-modules.sh | 12 - modules.d/dnbd3-rootfs/hooks/mount-root-device.sh | 26 - .../dnbd3-rootfs/hooks/prepare-root-partition.sh | 144 ---- .../dnbd3-rootfs/hooks/s3-copy-openslx-config.sh | 43 ++ modules.d/dnbd3-rootfs/hooks/s3-dnbd3root.sh | 153 ++++ modules.d/dnbd3-rootfs/hooks/s3-mount-root.sh | 39 + modules.d/dnbd3-rootfs/hooks/shutdown-umount.sh | 0 modules.d/dnbd3-rootfs/module-setup.sh | 66 +- modules.d/dnbd3-rootfs/services/dnbd3root.service | 12 - .../services/s3-copy-openslx-config.service | 11 + .../dnbd3-rootfs/services/s3-dnbd3root.service | 15 + .../dnbd3-rootfs/services/s3-mount-root.service | 12 + modules.d/haveged/module-setup.sh | 0 modules.d/slx-addons/hooks/s3-setup-addons.sh | 103 +++ modules.d/slx-addons/module-setup.sh | 15 +- modules.d/slx-addons/scripts/setup-addons.sh | 102 --- .../slx-addons/services/ldconfig-stage4.service | 8 + modules.d/slx-addons/services/ldconfig.service | 8 - .../slx-addons/services/s3-setup-addons.service | 11 + .../slx-clock/hooks/s3-configure-timesyncd.sh | 12 + modules.d/slx-clock/hooks/s3-ntp-sync.sh | 53 ++ modules.d/slx-clock/module-setup.sh | 22 +- modules.d/slx-clock/scripts/configure-timesyncd.sh | 12 - modules.d/slx-clock/scripts/ntp-sync.sh | 60 -- .../services/s3-configure-timesyncd.service | 12 + modules.d/slx-clock/services/s3-ntp-sync.service | 11 + modules.d/slx-dmsetup/hooks/dmsetup-slx-device | 794 +++++++++++++++++++++ modules.d/slx-dmsetup/hooks/s3-grow-rootfs.sh | 45 ++ modules.d/slx-dmsetup/module-setup.sh | 13 +- modules.d/slx-dmsetup/scripts/dmsetup-slx-device | 789 -------------------- modules.d/slx-dmsetup/scripts/grow-rootfs.sh | 45 -- .../slx-dmsetup/services/s3-grow-rootfs.service | 12 + modules.d/slx-drm/hooks/activate-nvidia-drivers.sh | 42 -- modules.d/slx-drm/hooks/copy-nvidia-drivers.sh | 28 - .../slx-drm/hooks/s3-activate-nvidia-drivers.sh | 42 ++ modules.d/slx-drm/hooks/s3-copy-nvidia-drivers.sh | 28 + modules.d/slx-drm/module-setup.sh | 12 +- .../services/s3-activate-nvidia-drivers.service | 13 + .../services/s3-copy-nvidia-drivers.service | 12 + .../slx-network/hooks/activate-bootif-dhcp.sh | 31 - .../slx-network/hooks/activate-stage4-dhcp.sh | 31 + .../slx-network/hooks/configure-jumbo-frames.sh | 0 modules.d/slx-network/hooks/copy-network-files.sh | 31 - .../slx-network/hooks/parse-ipxe-network-kcl.sh | 117 --- .../slx-network/hooks/s3-copy-network-files.sh | 31 + .../slx-network/hooks/s3-parse-network-kcl.sh | 119 +++ .../slx-network/hooks/s3-setup-bootif-network.sh | 186 +++++ modules.d/slx-network/module-setup.sh | 41 +- .../scripts/setup-bootif-network.stage3 | 172 ----- .../services/s3-copy-network-files.service | 12 + .../services/s3-parse-network-kcl.service | 13 + .../services/s3-setup-bootif-network.service | 13 + .../services/udhcpc-bootif-stage4.service | 12 + .../slx-network/services/udhcpc-bootif.service | 12 - modules.d/slx-runmode/hooks/s3-enable-runmode.sh | 13 + modules.d/slx-runmode/module-setup.sh | 11 +- modules.d/slx-runmode/scripts/runmode.sh | 13 - .../slx-runmode/services/s3-enable-runmode.service | 11 + modules.d/slx-splash/scripts/restore-cursor.sh | 0 modules.d/slx-splash/scripts/slx-splash-exam.sh | 0 .../slx-splash/scripts/slx-splash-icon-config.sh | 0 .../slx-splash/scripts/slx-splash-icon-hdd.sh | 0 .../slx-splash/scripts/slx-splash-icon-puzzle.sh | 0 .../slx-splash/scripts/slx-splash-icon-rootfs.sh | 0 modules.d/slx-splash/scripts/slx-splash-init.sh | 0 modules.d/slx-ssl/hooks/s3-ssl-sat.sh | 34 + modules.d/slx-ssl/hooks/setup-ssl-sat.sh | 34 - modules.d/slx-ssl/module-setup.sh | 11 +- modules.d/slx-ssl/services/s3-ssl-sat.service | 12 + modules.d/slx-tools/module-setup.sh | 0 .../slx-uuid/hooks/copy-system-uuid-to-newroot.sh | 6 + modules.d/slx-uuid/hooks/s3-get-system-uuid.sh | 71 ++ modules.d/slx-uuid/module-setup.sh | 29 +- .../scripts/copy-system-uuid-to-newroot.sh | 6 - modules.d/slx-uuid/scripts/get-system-uuid.sh | 69 -- .../slx-uuid/services/s3-get-system-uuid.service | 13 + .../hooks/configure-dhcp-for-newroot.sh | 0 .../hooks/copy-networkd-files-to-newroot.sh | 0 .../hooks/parse-kcl-for-networkd.sh | 0 99 files changed, 2383 insertions(+), 2077 deletions(-) mode change 100644 => 100755 modules.d/busybox/install-busybox-stage4.sh mode change 100644 => 100755 modules.d/busybox/install-busybox.sh mode change 100644 => 100755 modules.d/busybox/module-setup.sh delete mode 100755 modules.d/conf-tgz/hooks/fetch-config-tgz.sh create mode 100755 modules.d/conf-tgz/hooks/s3-fetch-config-tgz.sh create mode 100755 modules.d/conf-tgz/hooks/s3-fetch-config.sh create mode 100755 modules.d/conf-tgz/hooks/s3-unpack-config-tgz.sh delete mode 100755 modules.d/conf-tgz/hooks/unpack-config-tgz.sh create mode 100644 modules.d/conf-tgz/services/s3-fetch-config-tgz.service create mode 100644 modules.d/conf-tgz/services/s3-fetch-config.service create mode 100644 modules.d/conf-tgz/services/s3-unpack-config-tgz.service create mode 100644 modules.d/dnbd3-rootfs/configuration/systemd-newroot.conf delete mode 100755 modules.d/dnbd3-rootfs/hooks/copy-dnbd3-files-into-newroot.sh delete mode 100755 modules.d/dnbd3-rootfs/hooks/copy-openslx-configuration-into-newroot.sh delete mode 100755 modules.d/dnbd3-rootfs/hooks/fetch-config.sh delete mode 100755 modules.d/dnbd3-rootfs/hooks/load-custom-kernel-modules.sh delete mode 100755 modules.d/dnbd3-rootfs/hooks/mount-root-device.sh delete mode 100755 modules.d/dnbd3-rootfs/hooks/prepare-root-partition.sh create mode 100755 modules.d/dnbd3-rootfs/hooks/s3-copy-openslx-config.sh create mode 100755 modules.d/dnbd3-rootfs/hooks/s3-dnbd3root.sh create mode 100755 modules.d/dnbd3-rootfs/hooks/s3-mount-root.sh mode change 100644 => 100755 modules.d/dnbd3-rootfs/hooks/shutdown-umount.sh delete mode 100644 modules.d/dnbd3-rootfs/services/dnbd3root.service create mode 100644 modules.d/dnbd3-rootfs/services/s3-copy-openslx-config.service create mode 100644 modules.d/dnbd3-rootfs/services/s3-dnbd3root.service create mode 100644 modules.d/dnbd3-rootfs/services/s3-mount-root.service mode change 100644 => 100755 modules.d/haveged/module-setup.sh create mode 100755 modules.d/slx-addons/hooks/s3-setup-addons.sh delete mode 100644 modules.d/slx-addons/scripts/setup-addons.sh create mode 100644 modules.d/slx-addons/services/ldconfig-stage4.service delete mode 100644 modules.d/slx-addons/services/ldconfig.service create mode 100644 modules.d/slx-addons/services/s3-setup-addons.service create mode 100755 modules.d/slx-clock/hooks/s3-configure-timesyncd.sh create mode 100755 modules.d/slx-clock/hooks/s3-ntp-sync.sh delete mode 100644 modules.d/slx-clock/scripts/configure-timesyncd.sh delete mode 100755 modules.d/slx-clock/scripts/ntp-sync.sh create mode 100644 modules.d/slx-clock/services/s3-configure-timesyncd.service create mode 100644 modules.d/slx-clock/services/s3-ntp-sync.service create mode 100755 modules.d/slx-dmsetup/hooks/dmsetup-slx-device create mode 100755 modules.d/slx-dmsetup/hooks/s3-grow-rootfs.sh delete mode 100755 modules.d/slx-dmsetup/scripts/dmsetup-slx-device delete mode 100644 modules.d/slx-dmsetup/scripts/grow-rootfs.sh create mode 100644 modules.d/slx-dmsetup/services/s3-grow-rootfs.service delete mode 100644 modules.d/slx-drm/hooks/activate-nvidia-drivers.sh delete mode 100644 modules.d/slx-drm/hooks/copy-nvidia-drivers.sh create mode 100755 modules.d/slx-drm/hooks/s3-activate-nvidia-drivers.sh create mode 100755 modules.d/slx-drm/hooks/s3-copy-nvidia-drivers.sh create mode 100644 modules.d/slx-drm/services/s3-activate-nvidia-drivers.service create mode 100644 modules.d/slx-drm/services/s3-copy-nvidia-drivers.service delete mode 100644 modules.d/slx-network/hooks/activate-bootif-dhcp.sh create mode 100755 modules.d/slx-network/hooks/activate-stage4-dhcp.sh mode change 100644 => 100755 modules.d/slx-network/hooks/configure-jumbo-frames.sh delete mode 100644 modules.d/slx-network/hooks/copy-network-files.sh delete mode 100644 modules.d/slx-network/hooks/parse-ipxe-network-kcl.sh create mode 100755 modules.d/slx-network/hooks/s3-copy-network-files.sh create mode 100755 modules.d/slx-network/hooks/s3-parse-network-kcl.sh create mode 100755 modules.d/slx-network/hooks/s3-setup-bootif-network.sh delete mode 100755 modules.d/slx-network/scripts/setup-bootif-network.stage3 create mode 100644 modules.d/slx-network/services/s3-copy-network-files.service create mode 100644 modules.d/slx-network/services/s3-parse-network-kcl.service create mode 100644 modules.d/slx-network/services/s3-setup-bootif-network.service create mode 100644 modules.d/slx-network/services/udhcpc-bootif-stage4.service delete mode 100644 modules.d/slx-network/services/udhcpc-bootif.service create mode 100755 modules.d/slx-runmode/hooks/s3-enable-runmode.sh delete mode 100644 modules.d/slx-runmode/scripts/runmode.sh create mode 100644 modules.d/slx-runmode/services/s3-enable-runmode.service mode change 100644 => 100755 modules.d/slx-splash/scripts/restore-cursor.sh mode change 100644 => 100755 modules.d/slx-splash/scripts/slx-splash-exam.sh mode change 100644 => 100755 modules.d/slx-splash/scripts/slx-splash-icon-config.sh mode change 100644 => 100755 modules.d/slx-splash/scripts/slx-splash-icon-hdd.sh mode change 100644 => 100755 modules.d/slx-splash/scripts/slx-splash-icon-puzzle.sh mode change 100644 => 100755 modules.d/slx-splash/scripts/slx-splash-icon-rootfs.sh mode change 100644 => 100755 modules.d/slx-splash/scripts/slx-splash-init.sh create mode 100755 modules.d/slx-ssl/hooks/s3-ssl-sat.sh delete mode 100644 modules.d/slx-ssl/hooks/setup-ssl-sat.sh create mode 100644 modules.d/slx-ssl/services/s3-ssl-sat.service mode change 100644 => 100755 modules.d/slx-tools/module-setup.sh create mode 100755 modules.d/slx-uuid/hooks/copy-system-uuid-to-newroot.sh create mode 100755 modules.d/slx-uuid/hooks/s3-get-system-uuid.sh delete mode 100644 modules.d/slx-uuid/scripts/copy-system-uuid-to-newroot.sh delete mode 100644 modules.d/slx-uuid/scripts/get-system-uuid.sh create mode 100644 modules.d/slx-uuid/services/s3-get-system-uuid.service mode change 100644 => 100755 modules.d/systemd-networkd-ext/hooks/configure-dhcp-for-newroot.sh mode change 100644 => 100755 modules.d/systemd-networkd-ext/hooks/copy-networkd-files-to-newroot.sh mode change 100644 => 100755 modules.d/systemd-networkd-ext/hooks/parse-kcl-for-networkd.sh diff --git a/build-initramfs.sh b/build-initramfs.sh index e6f435bc..e41a642a 100755 --- a/build-initramfs.sh +++ b/build-initramfs.sh @@ -42,8 +42,8 @@ fi declare -A core_repo=( [handler]="git" [path]="$_repo_dir" - [url]="git://git.openslx.org/openslx-ng/systemd-init.git" - [branch]="downloader-cmake-refactor" + [url]="https://git.openslx.org/openslx-ng/systemd-init.git" + [branch]="master" ) declare -A core_dracut=( @@ -55,7 +55,7 @@ declare -A core_dracut=( declare -A module_dnbd3=( [handler]="git" [path]="${_repo_dir}/modules.d/dnbd3-rootfs/binaries/dnbd3" - [url]="git://git.openslx.org/dnbd3.git" + [url]="https://git.openslx.org/dnbd3.git" ) declare -A module_xloop=( diff --git a/dev-tools/exampleDracutModule/source/usr/lib/systemd/system/example.service b/dev-tools/exampleDracutModule/source/usr/lib/systemd/system/example.service index 83310c1d..666cc9c3 100644 --- a/dev-tools/exampleDracutModule/source/usr/lib/systemd/system/example.service +++ b/dev-tools/exampleDracutModule/source/usr/lib/systemd/system/example.service @@ -6,5 +6,4 @@ DefaultDependencies=no [Service] Type=oneshot RemainAfterExit=true -KillMode=none ExecStart=/usr/bin/bash -c '/usr/bin/echo test >/tmp/test-output.txt' diff --git a/dev-tools/exampleDracutModule/source/usr/lib/systemd/system/multi-user.target.wants/example.service b/dev-tools/exampleDracutModule/source/usr/lib/systemd/system/multi-user.target.wants/example.service index 83310c1d..666cc9c3 100644 --- a/dev-tools/exampleDracutModule/source/usr/lib/systemd/system/multi-user.target.wants/example.service +++ b/dev-tools/exampleDracutModule/source/usr/lib/systemd/system/multi-user.target.wants/example.service @@ -6,5 +6,4 @@ DefaultDependencies=no [Service] Type=oneshot RemainAfterExit=true -KillMode=none ExecStart=/usr/bin/bash -c '/usr/bin/echo test >/tmp/test-output.txt' diff --git a/modules.d/busybox/install-busybox-stage4.sh b/modules.d/busybox/install-busybox-stage4.sh old mode 100644 new mode 100755 diff --git a/modules.d/busybox/install-busybox.sh b/modules.d/busybox/install-busybox.sh old mode 100644 new mode 100755 index 6848a735..d4606590 --- a/modules.d/busybox/install-busybox.sh +++ b/modules.d/busybox/install-busybox.sh @@ -1,7 +1,7 @@ #!/bin/bash -if hash busybox; then - busybox --install -s +if hash busybox && busybox --install -s; then + echo "Busybox installed" else . /lib/dracut-lib.sh emergency_shell "Failed to initialize busybox, things will go wrong..." diff --git a/modules.d/busybox/module-setup.sh b/modules.d/busybox/module-setup.sh old mode 100644 new mode 100755 diff --git a/modules.d/conf-tgz/hooks/fetch-config-tgz.sh b/modules.d/conf-tgz/hooks/fetch-config-tgz.sh deleted file mode 100755 index 5fa96de7..00000000 --- a/modules.d/conf-tgz/hooks/fetch-config-tgz.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash -# -*- coding: utf-8 -*- - -type emergency_shell >/dev/null 2>&1 || source /lib/dracut-lib.sh - -. /etc/openslx - -if [ -z "$SLX_NO_CONFIG_TGZ" ]; then - # build config.tgz url from SLX_KCL_SERVERS and SLX_BASE_PATH - # both are written to /etc/openslx by dnbd3-rootfs's fetch-config.sh script - conftgz_url="http://${SLX_KCL_SERVERS}/${SLX_BASE_PATH}/config.tgz" - - # check if system's uuid was set - if [ -s "/run/system-uuid" ]; then - uuid="$(cat /run/system-uuid)" - if [ -n "$uuid" ]; then - conftgz_url="${conftgz_url}?uuid=${uuid}" - fi - fi - info "Download config.tgz from '$conftgz_url'..." - slx-tools download_retry --slx-time 20 -sS "${conftgz_url}" > "/etc/config.tgz" - - if [[ ! -s "/etc/config.tgz" ]]; then - warn "Failed to download '${conftgz_url}'!" - fi -fi -: diff --git a/modules.d/conf-tgz/hooks/s3-fetch-config-tgz.sh b/modules.d/conf-tgz/hooks/s3-fetch-config-tgz.sh new file mode 100755 index 00000000..2ed5f579 --- /dev/null +++ b/modules.d/conf-tgz/hooks/s3-fetch-config-tgz.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# -*- coding: utf-8 -*- + +type emergency_shell >/dev/null 2>&1 || source /lib/dracut-lib.sh + +. /etc/openslx + +if [ -z "$SLX_NO_CONFIG_TGZ" ]; then + # build config.tgz url from SLX_KCL_SERVERS and SLX_BASE_PATH + # both are written to /etc/openslx by dnbd3-rootfs's s3-fetch-config.sh script + conftgz_url="http://${SLX_KCL_SERVERS}/${SLX_BASE_PATH}/config.tgz" + + # check if system's uuid was set + if [ -s "/run/system-uuid" ]; then + uuid="$(cat /run/system-uuid)" + if [ -n "$uuid" ]; then + conftgz_url="${conftgz_url}?uuid=${uuid}" + fi + fi + info "Download config.tgz from '$conftgz_url'..." + slx-tools download_retry --slx-time 20 -sS "${conftgz_url}" > "/etc/config.tgz" + + if [[ ! -s "/etc/config.tgz" ]]; then + warn "Failed to download '${conftgz_url}'!" + fi +fi +: diff --git a/modules.d/conf-tgz/hooks/s3-fetch-config.sh b/modules.d/conf-tgz/hooks/s3-fetch-config.sh new file mode 100755 index 00000000..021a4c06 --- /dev/null +++ b/modules.d/conf-tgz/hooks/s3-fetch-config.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# -*- coding: utf-8 -*- +# region imports +type emergency_shell >/dev/null 2>&1 || source /lib/dracut-lib.sh +# endregion + +slx_server="$(getarg slxsrv=)" +slx_server_base="$(getarg slxbase=)" + +# if no slxsrv was specified, use the server the kernel was +# downloaded from as fallback. Mostly for legacy PXE boot. +if [ -z "$slx_server" ]; then + slx_server="$(getarg BOOT_IMAGE= | awk -F[/:] '{print $4}')" +fi +if [ -z "$slx_server" ]; then + # use tftp server from ip parameter + slx_server="$(getarg ip= | awk -F: '{print $2}' )" +fi +if [ -z "$slx_server" ]; then + # we have a problem :/ + emergency_shell "Failed to determine SLX server to fetch config from." +fi + +# build config_url +config_url="http://${slx_server#@}/${slx_server_base}/config" + +# check if system's uuid was set +if [ -s "/run/system-uuid" ]; then + uuid=$(cat "/run/system-uuid") + if [ -n "$uuid" ]; then + config_url="${config_url}?uuid=${uuid}" + fi +fi + +config_path="/etc/openslx.tmp" + +echo "Downloading '$config_url'..." +slx-tools download_retry --slx-time 20 -sS "$config_url" > "$config_path" +return_code="$?" + +if [ ! -s "$config_path" ] ; then + emergency_shell "Downloading netboot configuration file from '$config_url' failed with: $return_code" +fi + +if ! ash -n "$config_path"; then + emergency_shell "Netboot configuration failed syntax check!" +fi + +# remember kcl server and base +{ + echo "SLX_KCL_SERVERS='$slx_server'" + echo "SLX_BASE_PATH='$slx_server_base'" + echo "# Config fetched from $config_url" + echo "CONFIG_DOWNLOAD_TIME=$(sed -r 's/^([0-9]+)\.([0-9]+).*$/\1\2/' /proc/uptime)" + echo '#_RCONFIG_TAG' +} > /etc/openslx + +# finally copy remote config into it +cat "$config_path" >> /etc/openslx + +# slxsrv overrides SLX_DNBD3_SERVERS if prefixed with @ +[ "${slx_server#@}" != "${slx_server}" ] && sed -i "s/^SLX_DNBD3_SERVERS=.*/SLX_DNBD3_SERVERS='${slx_server#@}'/" "/etc/openslx" +true diff --git a/modules.d/conf-tgz/hooks/s3-unpack-config-tgz.sh b/modules.d/conf-tgz/hooks/s3-unpack-config-tgz.sh new file mode 100755 index 00000000..2132423c --- /dev/null +++ b/modules.d/conf-tgz/hooks/s3-unpack-config-tgz.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# -*- coding: utf-8 -*- + +type emergency_shell >/dev/null 2>&1 || source /lib/dracut-lib.sh + +# tarcopy +tarcopy() { + [ -d "$1" -a -d "$2" ] || return 1 + cd "$1" + local filelist="$(mktemp)" + find . \! -type d > "$filelist" + tar -c -p -T "$filelist" | tar -xp -C "$2" + rm -f -- "$filelist" + cd - &>/dev/null +} + +unpack_config_tgz() { + local config_tgz="/etc/config.tgz" + [ -e "$config_tgz" ] || return 1 + # create list of overwritten files for debugging purposes + mkdir -p "${NEWROOT}/opt/openslx" + tar \ + --list \ + --verbose \ + --file="$config_tgz" \ + > "${NEWROOT}/opt/openslx/config.tgz.list" 2>&1 + local extract_dir="$(mktemp -d)" + tar \ + --extract \ + --preserve-permissions \ + --file="$config_tgz" \ + --directory="$extract_dir" + if [ "$?" -ne 0 ]; then + warn "Failed to extract '$config_tgz' to '$extract_dir'." + return 1 + fi + # check SLX_LOCAL_CONFIGURATION + source "/etc/openslx" + if [ -n "$SLX_LOCAL_CONFIGURATION" ]; then + if [ ! -d "${extract_dir}/openslx-configs/${SLX_LOCAL_CONFIGURATION}" ]; then + warn "Ignoring missing SLX_LOCAL_CONFIGURATION in '$config_tgz'." + else + tarcopy \ + "${extract_dir}/openslx-configs/${SLX_LOCAL_CONFIGURATION}" \ + "${extract_dir}" + fi + fi + # always purge openslx-configs/ + rm -rf "${extract_dir}/openslx-configs" + + # finally copy the rest into stage4 + if ! tarcopy "${extract_dir}" "$NEWROOT"; then + warn "'tarcopy' from '$extract_dir' to '$NEWROOT' failed." + fi +} + +unpack_config_tgz +: diff --git a/modules.d/conf-tgz/hooks/unpack-config-tgz.sh b/modules.d/conf-tgz/hooks/unpack-config-tgz.sh deleted file mode 100755 index 2132423c..00000000 --- a/modules.d/conf-tgz/hooks/unpack-config-tgz.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env bash -# -*- coding: utf-8 -*- - -type emergency_shell >/dev/null 2>&1 || source /lib/dracut-lib.sh - -# tarcopy -tarcopy() { - [ -d "$1" -a -d "$2" ] || return 1 - cd "$1" - local filelist="$(mktemp)" - find . \! -type d > "$filelist" - tar -c -p -T "$filelist" | tar -xp -C "$2" - rm -f -- "$filelist" - cd - &>/dev/null -} - -unpack_config_tgz() { - local config_tgz="/etc/config.tgz" - [ -e "$config_tgz" ] || return 1 - # create list of overwritten files for debugging purposes - mkdir -p "${NEWROOT}/opt/openslx" - tar \ - --list \ - --verbose \ - --file="$config_tgz" \ - > "${NEWROOT}/opt/openslx/config.tgz.list" 2>&1 - local extract_dir="$(mktemp -d)" - tar \ - --extract \ - --preserve-permissions \ - --file="$config_tgz" \ - --directory="$extract_dir" - if [ "$?" -ne 0 ]; then - warn "Failed to extract '$config_tgz' to '$extract_dir'." - return 1 - fi - # check SLX_LOCAL_CONFIGURATION - source "/etc/openslx" - if [ -n "$SLX_LOCAL_CONFIGURATION" ]; then - if [ ! -d "${extract_dir}/openslx-configs/${SLX_LOCAL_CONFIGURATION}" ]; then - warn "Ignoring missing SLX_LOCAL_CONFIGURATION in '$config_tgz'." - else - tarcopy \ - "${extract_dir}/openslx-configs/${SLX_LOCAL_CONFIGURATION}" \ - "${extract_dir}" - fi - fi - # always purge openslx-configs/ - rm -rf "${extract_dir}/openslx-configs" - - # finally copy the rest into stage4 - if ! tarcopy "${extract_dir}" "$NEWROOT"; then - warn "'tarcopy' from '$extract_dir' to '$NEWROOT' failed." - fi -} - -unpack_config_tgz -: diff --git a/modules.d/conf-tgz/module-setup.sh b/modules.d/conf-tgz/module-setup.sh index e6edd1e0..29aae9ea 100755 --- a/modules.d/conf-tgz/module-setup.sh +++ b/modules.d/conf-tgz/module-setup.sh @@ -1,14 +1,26 @@ #!/usr/bin/env bash + check() { - return 255 + return 255 } depends() { - echo dnbd3-rootfs slx-tools + echo dnbd3-rootfs slx-tools } install() { - inst_hook pre-mount 20 "$moddir/hooks/fetch-config-tgz.sh" - # do NOT FUCKING CHANGE THIS - inst_hook pre-pivot 90 "$moddir/hooks/unpack-config-tgz.sh" - # TODO check if busybox is enough - inst_multiple tar mktemp gzip + inst_multiple tar mktemp gzip + + for _name in "s3-fetch-config" "s3-fetch-config-tgz" "s3-unpack-config-tgz"; do + inst "$moddir/hooks/${_name}.sh" \ + "/usr/local/bin/${_name}.sh" + inst_simple "${moddir}/services/${_name}.service" \ + "${systemdsystemunitdir}/${_name}.service" + mkdir --parents \ + "${initdir}/${systemdsystemunitdir}/dracut-pre-pivot.service.requires" + ln_r "${systemdsystemunitdir}/${_name}.service" \ + "${systemdsystemunitdir}/dracut-pre-pivot.service.requires/${_name}.service" + mkdir --parents \ + "${initdir}/${systemdsystemunitdir}/dracut-mount.service.wants" + ln_r "${systemdsystemunitdir}/${_name}.service" \ + "${systemdsystemunitdir}/dracut-mount.service.wants/${_name}.service" + done } diff --git a/modules.d/conf-tgz/services/s3-fetch-config-tgz.service b/modules.d/conf-tgz/services/s3-fetch-config-tgz.service new file mode 100644 index 00000000..60d2b774 --- /dev/null +++ b/modules.d/conf-tgz/services/s3-fetch-config-tgz.service @@ -0,0 +1,13 @@ +[Unit] +Description=Download config.tgz +Requires=s3-setup-bootif-network.service +After=s3-setup-bootif-network.service +Wants=s3-fetch-config.service +After=s3-fetch-config.service +DefaultDependencies=no +IgnoreOnIsolate=true + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/bin/s3-fetch-config-tgz.sh diff --git a/modules.d/conf-tgz/services/s3-fetch-config.service b/modules.d/conf-tgz/services/s3-fetch-config.service new file mode 100644 index 00000000..9d602899 --- /dev/null +++ b/modules.d/conf-tgz/services/s3-fetch-config.service @@ -0,0 +1,13 @@ +[Unit] +Description=Download config +Requires=s3-setup-bootif-network.service +After=s3-setup-bootif-network.service +Wants=s3-get-system-uuid.service +After=s3-get-system-uuid.service +DefaultDependencies=no +IgnoreOnIsolate=true + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/bin/s3-fetch-config.sh diff --git a/modules.d/conf-tgz/services/s3-unpack-config-tgz.service b/modules.d/conf-tgz/services/s3-unpack-config-tgz.service new file mode 100644 index 00000000..d3b9a3e0 --- /dev/null +++ b/modules.d/conf-tgz/services/s3-unpack-config-tgz.service @@ -0,0 +1,12 @@ +[Unit] +Description=Unpack config.tgz to stage 4 +After=initrd-root-fs.target +Requires=s3-fetch-config-tgz.service +After=s3-fetch-config-tgz.service +DefaultDependencies=no +IgnoreOnIsolate=true + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/bin/s3-unpack-config-tgz.sh diff --git a/modules.d/dnbd3-rootfs/configuration/systemd-newroot.conf b/modules.d/dnbd3-rootfs/configuration/systemd-newroot.conf new file mode 100644 index 00000000..6e02fad5 --- /dev/null +++ b/modules.d/dnbd3-rootfs/configuration/systemd-newroot.conf @@ -0,0 +1,2 @@ +[Manager] +DefaultEnvironment="NEWROOT=/sysroot" diff --git a/modules.d/dnbd3-rootfs/hooks/copy-dnbd3-files-into-newroot.sh b/modules.d/dnbd3-rootfs/hooks/copy-dnbd3-files-into-newroot.sh deleted file mode 100755 index 2ff8664f..00000000 --- a/modules.d/dnbd3-rootfs/hooks/copy-dnbd3-files-into-newroot.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash -# -*- coding: utf-8 -*- - -# copy dnbd3root.service to NEWROOT to ensure it stays known to systemd -systemd_system_unit_path="$(dirname \ - "$(systemctl show -p FragmentPath dracut-mount.service | cut -c 14-)")" -new_systemd_system_unit_path="${NEWROOT}/lib/systemd/system" -cp "${systemd_system_unit_path}/dnbd3root.service" \ - "${new_systemd_system_unit_path}/dnbd3root.service" -mkdir --parents "${new_systemd_system_unit_path}/sysinit.target.wants" -ln --symbolic '../dnbd3root.service' \ - "${new_systemd_system_unit_path}/sysinit.target.wants/dnbd3root.service" - -# copy dnbd3-client binary to NEWROOT to make sure it is compatible with the -# kernel module -dnbd3_client_path="$(type -p dnbd3-client)" -if [ -n "$dnbd3_client_path" ]; then - mkdir -p "${NEWROOT}/${dnbd3_client_path%/*}" - cp "$dnbd3_client_path" "${NEWROOT}/${dnbd3_client_path}" -fi -true diff --git a/modules.d/dnbd3-rootfs/hooks/copy-dracut-systemd-files-into-newroot.sh b/modules.d/dnbd3-rootfs/hooks/copy-dracut-systemd-files-into-newroot.sh index d0e847ca..58f23207 100755 --- a/modules.d/dnbd3-rootfs/hooks/copy-dracut-systemd-files-into-newroot.sh +++ b/modules.d/dnbd3-rootfs/hooks/copy-dracut-systemd-files-into-newroot.sh @@ -2,27 +2,20 @@ # -*- coding: utf-8 -*- type emergency_shell >/dev/null 2>&1 || source /lib/dracut-lib.sh -# Dracut may not be installed on the new root. Thus copy all services over. -dracut_mount_unit_path="$(systemctl show -p FragmentPath dracut-mount.service \ - | cut -c 14-)" -systemd_system_unit_path="${dracut_mount_unit_path%/*}" +# Copy out services over to stage 4, so they still appear in +# systemd-analyze plot etc. new_systemd_system_unit_path="${NEWROOT}/lib/systemd/system" mkdir --parents "$new_systemd_system_unit_path/initrd.target.wants" -for file in \ - dracut-cmdline.service \ - dracut-initqueue.service \ - dracut-mount.service \ - dracut-pre-mount.service \ - dracut-pre-pivot.service \ - dracut-pre-trigger.service \ - dracut-pre-udev.service -do - cp "${systemd_system_unit_path}/${file}" \ - "${new_systemd_system_unit_path}/${file}" - # "ln" returns an error if the link already exists. - source_path="../${file}" - target_path="${new_systemd_system_unit_path}/initrd.target.wants/${file}" - ln --symbolic "$source_path" "$target_path" &>/dev/null || \ - warn "Failed to link \"$source_path\" to \"$target_path\"." + +for dir in /run/systemd/system /lib/systemd/system /etc/systemd/system; do + for file in "$dir"/s3-*.{service,target} "$dir"/dracut-*.{service,target}; do + [ -f "$file" ] || continue + name="${file##*/}" + cp "${file}" "${new_systemd_system_unit_path}/${name}" + source_path="../${name}" + target_path="${new_systemd_system_unit_path}/initrd.target.wants/${name}" + ln -nfs "$source_path" "$target_path" || \ + warn "Failed to link \"$source_path\" to \"$target_path\"." + done done diff --git a/modules.d/dnbd3-rootfs/hooks/copy-openslx-configuration-into-newroot.sh b/modules.d/dnbd3-rootfs/hooks/copy-openslx-configuration-into-newroot.sh deleted file mode 100755 index 7e2b74e3..00000000 --- a/modules.d/dnbd3-rootfs/hooks/copy-openslx-configuration-into-newroot.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash -# -*- coding: utf-8 -*- -type emergency_shell >/dev/null 2>&1 || source /lib/dracut-lib.sh - -source "/etc/openslx" - -# Merge search domain from server and from dhcp -( - search="$SLX_NET_SEARCH" - source /run/openslx/network.conf - search="$SLX_NET_SEARCH $search" # DHCP > config vars - declare -a uniq - declare -A dups # Don't use keys for output later as order is undefined - for i in $search; do - [ -n "${dups["$i"]}" ] && continue - dups["$i"]=1 - uniq+=( "$i" ) - done - sed -i 's/^SLX_NET_SEARCH=/# &/' "/etc/openslx" "/run/openslx/network.conf" - echo "SLX_NET_SEARCH='${uniq[*]}'" >> "/run/openslx/network.conf" - if grep -q '^search' "/etc/resolv.conf"; then - sed -i "s/^search.*$/search ${uniq[*]}/" "/etc/resolv.conf" - else - echo "search ${uniq[*]}" "/etc/resolv.conf" - fi -) - - -mkdir --parents "${NEWROOT}/opt/openslx" -cp "/etc/openslx" "${NEWROOT}/opt/openslx/config" - -echo "## Generated by '$0' in stage3" >> "${NEWROOT}/opt/openslx/config" -cat "/run/openslx/network.conf" >> "${NEWROOT}/opt/openslx/config" - -# Set root/demo password for stage4, if set -if [ -n "${SLX_ROOT_PASS}" ]; then - sed -i "s#^root:[^:]*:#root:$SLX_ROOT_PASS:#" "$NEWROOT/etc/shadow" -fi -if [ -n "${SLX_DEMO_PASS}" ] && grep -q '^demo' "$NEWROOT/etc/shadow"; then - sed -i "s#^demo:[^:]*:#demo:$SLX_DEMO_PASS:#" "$NEWROOT/etc/shadow" -fi diff --git a/modules.d/dnbd3-rootfs/hooks/fetch-config.sh b/modules.d/dnbd3-rootfs/hooks/fetch-config.sh deleted file mode 100755 index 021a4c06..00000000 --- a/modules.d/dnbd3-rootfs/hooks/fetch-config.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env bash -# -*- coding: utf-8 -*- -# region imports -type emergency_shell >/dev/null 2>&1 || source /lib/dracut-lib.sh -# endregion - -slx_server="$(getarg slxsrv=)" -slx_server_base="$(getarg slxbase=)" - -# if no slxsrv was specified, use the server the kernel was -# downloaded from as fallback. Mostly for legacy PXE boot. -if [ -z "$slx_server" ]; then - slx_server="$(getarg BOOT_IMAGE= | awk -F[/:] '{print $4}')" -fi -if [ -z "$slx_server" ]; then - # use tftp server from ip parameter - slx_server="$(getarg ip= | awk -F: '{print $2}' )" -fi -if [ -z "$slx_server" ]; then - # we have a problem :/ - emergency_shell "Failed to determine SLX server to fetch config from." -fi - -# build config_url -config_url="http://${slx_server#@}/${slx_server_base}/config" - -# check if system's uuid was set -if [ -s "/run/system-uuid" ]; then - uuid=$(cat "/run/system-uuid") - if [ -n "$uuid" ]; then - config_url="${config_url}?uuid=${uuid}" - fi -fi - -config_path="/etc/openslx.tmp" - -echo "Downloading '$config_url'..." -slx-tools download_retry --slx-time 20 -sS "$config_url" > "$config_path" -return_code="$?" - -if [ ! -s "$config_path" ] ; then - emergency_shell "Downloading netboot configuration file from '$config_url' failed with: $return_code" -fi - -if ! ash -n "$config_path"; then - emergency_shell "Netboot configuration failed syntax check!" -fi - -# remember kcl server and base -{ - echo "SLX_KCL_SERVERS='$slx_server'" - echo "SLX_BASE_PATH='$slx_server_base'" - echo "# Config fetched from $config_url" - echo "CONFIG_DOWNLOAD_TIME=$(sed -r 's/^([0-9]+)\.([0-9]+).*$/\1\2/' /proc/uptime)" - echo '#_RCONFIG_TAG' -} > /etc/openslx - -# finally copy remote config into it -cat "$config_path" >> /etc/openslx - -# slxsrv overrides SLX_DNBD3_SERVERS if prefixed with @ -[ "${slx_server#@}" != "${slx_server}" ] && sed -i "s/^SLX_DNBD3_SERVERS=.*/SLX_DNBD3_SERVERS='${slx_server#@}'/" "/etc/openslx" -true diff --git a/modules.d/dnbd3-rootfs/hooks/load-custom-kernel-modules.sh b/modules.d/dnbd3-rootfs/hooks/load-custom-kernel-modules.sh deleted file mode 100755 index c9ea106a..00000000 --- a/modules.d/dnbd3-rootfs/hooks/load-custom-kernel-modules.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -# -*- coding: utf-8 -*- -type emergency_shell >/dev/null 2>&1 || source /lib/dracut-lib.sh -for kmod in dnbd3 \ - xloop \ - xloop_file_fmt_qcow \ - xloop_file_fmt_raw; do - if ! modprobe ${kmod}; then - warn "Failed to load kernel module: $kmod" - ! : - fi -done diff --git a/modules.d/dnbd3-rootfs/hooks/mount-root-device.sh b/modules.d/dnbd3-rootfs/hooks/mount-root-device.sh deleted file mode 100755 index 73d947d0..00000000 --- a/modules.d/dnbd3-rootfs/hooks/mount-root-device.sh +++ /dev/null @@ -1,26 +0,0 @@ -type emergency_shell >/dev/null 2>&1 || source /lib/dracut-lib.sh - -source "/etc/openslx" -ret=99 -if [ -n "$SLX_MOUNT_ROOT_OPTIONS" ]; then - # Always prefer mount options mandated by server - mount "$SLX_DNBD3_DEVICE_COW" "$NEWROOT" $SLX_MOUNT_ROOT_OPTIONS - ret=$? -else - # Let's guess it's ext4, tune for maximum performance as we don't care about recoverability - mount "$SLX_DNBD3_DEVICE_COW" "$NEWROOT" -o data=writeback,barrier=0,commit=60,noinit_itable,discard,noatime - ret=$? - if (( ret != 0 )); then - # Just try with no options and hope for the best... - mount "$SLX_DNBD3_DEVICE_COW" "$NEWROOT" - ret=$? - fi -fi - -if [ -n "$SLX_GENERATE_FSTAB_SCRIPT" ]; then - eval "$SLX_GENERATE_FSTAB_SCRIPT" -else - echo "" > "$NEWROOT/etc/fstab" -fi - -return $ret diff --git a/modules.d/dnbd3-rootfs/hooks/prepare-root-partition.sh b/modules.d/dnbd3-rootfs/hooks/prepare-root-partition.sh deleted file mode 100755 index bef92ce9..00000000 --- a/modules.d/dnbd3-rootfs/hooks/prepare-root-partition.sh +++ /dev/null @@ -1,144 +0,0 @@ -#!/usr/bin/env bash -type emergency_shell > /dev/null 2>&1 || source /lib/dracut-lib.sh -source /etc/openslx - -export PATH="/usr/local/bin:$PATH" -export LD_LIBRARY_PATH="/usr/local/lib" - - -# hardcode dnbd device path -declare -rg _dnbd3_dev="/dev/dnbd0" - -# all outputs are redirected to stderr, since this functions should -# only echo the path to the unpacked container to stdout. -container_unpack_xmount() { - local in_device="$1" - local out_path="/mnt/xmount" - mkdir -p "$out_path" - # check tools first - if ! hash xmount systemd-preserve-process-marker; then - warn "Missing xmount deps, will try raw..." 1>&2 - elif ! systemd-preserve-process-marker xmount \ - --in qemu "$in_device" \ - --out raw "$out_path" &>/dev/null; then - warn "xmount call failed, assuming raw image." 1>&2 - else - in_device="${out_path}/${_dnbd3_dev##*/}.dd" - fi - local out_device="$(losetup -f)" - if ! losetup "$out_device" "$in_device" --partscan; then - warn "Failed to attach '$in_device' to '$out_device'." - return 1 - fi - udevadm settle - echo "$out_device" -} - -container_unpack_xloop() { - local in_device="$1" - local out_device="$(xlosetup -f)" - if ! xlosetup -r -t QCOW "$out_device" "$in_device" --partscan; then - warn "Failed to attach '$in_device' to '$out_device'." - return - fi - udevadm settle - echo "$out_device" -} -# endregion - -# region connect dnbd3 image -# Determine path to dnbd3 image: either on the kcl or via config file -declare -r KCL_DNBD3_IMAGE="$(getarg slx.stage4.path=)" -if [ -n "$KCL_DNBD3_IMAGE" ]; then - SLX_DNBD3_IMAGE="$KCL_DNBD3_IMAGE" - echo "SLX_DNBD3_IMAGE='$SLX_DNBD3_IMAGE'" >> /etc/openslx -fi -if [ -z "$SLX_DNBD3_IMAGE" ]; then - emergency_shell "Failed to determine which DNBD3 image to use." \ - "It was neither specified on kernel command line nor in the" \ - "configuration file." -fi -declare -r KCL_DNBD3_RID="$(getarg slx.stage4.rid=)" -if [ -n "$KCL_DNBD3_RID" ]; then - # specified on the KCL? - SLX_DNBD3_RID="$KCL_DNBD3_RID" - echo "SLX_DNBD3_RID='$SLX_DNBD3_RID'" >> /etc/openslx -fi -if [ -n "$SLX_DNBD3_RID" ]; then - _dnbd3_client_additional_args=("--rid" "$SLX_DNBD3_RID") -fi - -IFS=", " -for try in {1..5} ""; do - if [ -z "$try" ]; then - emergency_shell "Failed to connect '${SLX_DNBD3_IMAGE}'" \ - "(revision: ${SLX_DNBD3_RID:-0})" \ - "from one of '$SLX_DNBD3_SERVERS' to '$_dnbd3_dev'." \ - "Check if the image exists on one of the servers" \ - "and if any is reachable from this client." - fi - info "Trying hosts '$SLX_DNBD3_SERVERS'." - if dnbd3-client \ - --host "$SLX_DNBD3_SERVERS" \ - --image "$SLX_DNBD3_IMAGE" \ - --device "$_dnbd3_dev" \ - "${_dnbd3_client_additional_args[@]}"; then - break - fi - sleep 1 -done - -# endregion -# region unpack dnbd3 image -if ! [[ $SLX_QCOW_HANDLER =~ ^(kernel|xloop|xmount)?$ ]]; then - emergency_shell "Unsupported QCOW handler: $SLX_QCOW_HANDLER" \ - "Use either 'xmount' or 'xloop'." -fi -if [ -z "$SLX_QCOW_HANDLER" ]; then - SLX_QCOW_HANDLER="xloop" - echo "SLX_QCOW_HANDLER='$SLX_QCOW_HANDLER'" >> /etc/openslx -fi -if [[ $SLX_QCOW_HANDLER =~ ^kernel|xloop$ ]]; then - read_only_device="$(container_unpack_xloop "$_dnbd3_dev")" -fi -if [ -z "$read_only_device" ]; then - read_only_device="$(container_unpack_xmount "$_dnbd3_dev")" -fi - -# Fail fast if unpacking dnbd3 image failed. -if [ -z "$read_only_device" ]; then - emergency_shell "Failed to unpack the qcow2 image!" -fi - -# endregion -# region find system partition within dnbd3 image -if [ -z "$SLX_SYSTEM_PARTITION_PREPARATION_SCRIPT" ]; then - # Find requested root partition - default to SLX_SYS label - read_only_partition="$( slx-tools dev_find_partitions \ - "$read_only_device" "${SLX_SYSTEM_PARTITION_IDENTIFIER:-SLX_SYS}" )" - if [ -z "$SLX_SYSTEM_PARTITION_IDENTIFIER" ]; then - if [ -n "$read_only_partition" ]; then - # Defaulting to SLX_SYS worked - echo "SLX_SYSTEM_PARTITION_IDENTIFIER='SLX_SYS'" >> /etc/openslx - else - # Aussume there is no partition table - read_only_partition="$read_only_device" - fi - fi - -else - eval "$SLX_SYSTEM_PARTITION_PREPARATION_SCRIPT" -fi -if [[ -z "$read_only_partition" ]]; then - warn "Failed to find unique device with identifier" \ - "\"${SLX_SYSTEM_PARTITION_IDENTIFIER}\"; matched devices:" \ - "\"${read_only_partition}\"" - exit 1 -fi -info "Using read-only partition: $read_only_partition" -# endregion - -# region add rw layer to dnbd3 image -# don't be fooled to think we are done, the next part is crucial -dmsetup-slx-device "$read_only_partition" -# endregion diff --git a/modules.d/dnbd3-rootfs/hooks/s3-copy-openslx-config.sh b/modules.d/dnbd3-rootfs/hooks/s3-copy-openslx-config.sh new file mode 100755 index 00000000..bedceb85 --- /dev/null +++ b/modules.d/dnbd3-rootfs/hooks/s3-copy-openslx-config.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# -*- coding: utf-8 -*- +type emergency_shell >/dev/null 2>&1 || source /lib/dracut-lib.sh + +source "/etc/openslx" + +# Merge search domain from server and from dhcp +( + search="$SLX_NET_SEARCH" + source /run/openslx/network.conf + search="$SLX_NET_SEARCH $search" # DHCP > config vars + declare -a uniq + declare -A dups # Don't use keys for output later as order is undefined + for i in $search; do + [ -n "${dups["$i"]}" ] && continue + dups["$i"]=1 + uniq+=( "$i" ) + done + sed -i 's/^SLX_NET_SEARCH=/# &/' "/etc/openslx" "/run/openslx/network.conf" + echo "SLX_NET_SEARCH='${uniq[*]}'" >> "/run/openslx/network.conf" + if grep -q '^search' "/etc/resolv.conf"; then + sed -i "s/^search.*$/search ${uniq[*]}/" "/etc/resolv.conf" + else + echo "search ${uniq[*]}" "/etc/resolv.conf" + fi +) + + +mkdir --parents "${NEWROOT}/opt/openslx" +{ + cat "/etc/openslx" + echo "## Generated by '$0' in stage3" + cat "/run/openslx/network.conf" +} > "${NEWROOT}/opt/openslx/config" +ln -nfs "${NEWROOT}/opt/openslx/config" "/etc/openslx" + +# Set root/demo password for stage4, if set +if [ -n "${SLX_ROOT_PASS}" ]; then + sed -i "s#^root:[^:]*:#root:$SLX_ROOT_PASS:#" "$NEWROOT/etc/shadow" +fi +if [ -n "${SLX_DEMO_PASS}" ] && grep -q '^demo:' "$NEWROOT/etc/shadow"; then + sed -i "s#^demo:[^:]*:#demo:$SLX_DEMO_PASS:#" "$NEWROOT/etc/shadow" +fi diff --git a/modules.d/dnbd3-rootfs/hooks/s3-dnbd3root.sh b/modules.d/dnbd3-rootfs/hooks/s3-dnbd3root.sh new file mode 100755 index 00000000..01a8c32c --- /dev/null +++ b/modules.d/dnbd3-rootfs/hooks/s3-dnbd3root.sh @@ -0,0 +1,153 @@ +#!/usr/bin/env bash +type emergency_shell > /dev/null 2>&1 || source /lib/dracut-lib.sh +source /etc/openslx + +export PATH="/usr/local/bin:$PATH" +export LD_LIBRARY_PATH="/usr/local/lib" + + +# hardcode dnbd device path +declare -rg _dnbd3_dev="/dev/dnbd0" + +# all outputs are redirected to stderr, since this functions should +# only echo the path to the unpacked container to stdout. +container_unpack_xmount() { + local in_device="$1" + local out_path="/mnt/xmount" + mkdir -p "$out_path" + # check tools first + if ! hash xmount systemd-preserve-process-marker; then + warn "Missing xmount deps, will try raw..." 1>&2 + elif ! systemd-preserve-process-marker xmount \ + --in qemu "$in_device" \ + --out raw "$out_path" &>/dev/null; then + warn "xmount call failed, assuming raw image." 1>&2 + else + in_device="${out_path}/${_dnbd3_dev##*/}.dd" + fi + local out_device="$(losetup -f)" + if ! losetup "$out_device" "$in_device" --partscan; then + warn "Failed to attach '$in_device' to '$out_device'." + return 1 + fi + udevadm settle + echo "$out_device" +} + +container_unpack_xloop() { + local in_device="$1" + local out_device="$(xlosetup -f)" + for kmod in xloop xloop_file_fmt_qcow xloop_file_fmt_raw; do + if ! modprobe "${kmod}"; then + warn "Failed to load kernel module: $kmod" + fi + done + if ! xlosetup -r -t QCOW "$out_device" "$in_device" --partscan; then + warn "Failed to attach '$in_device' to '$out_device'." + return + fi + udevadm settle + echo "$out_device" +} +# endregion + +# region connect dnbd3 image +# Determine path to dnbd3 image: either on the kcl or via config file +declare -r KCL_DNBD3_IMAGE="$(getarg slx.stage4.path=)" +if [ -n "$KCL_DNBD3_IMAGE" ]; then + SLX_DNBD3_IMAGE="$KCL_DNBD3_IMAGE" + echo "SLX_DNBD3_IMAGE='$SLX_DNBD3_IMAGE'" >> /etc/openslx +fi +if [ -z "$SLX_DNBD3_IMAGE" ]; then + emergency_shell "Failed to determine which DNBD3 image to use." \ + "It was neither specified on kernel command line nor in the" \ + "configuration file." +fi +declare -r KCL_DNBD3_RID="$(getarg slx.stage4.rid=)" +if [ -n "$KCL_DNBD3_RID" ]; then + # specified on the KCL? + SLX_DNBD3_RID="$KCL_DNBD3_RID" + echo "SLX_DNBD3_RID='$SLX_DNBD3_RID'" >> /etc/openslx +fi +if [ -n "$SLX_DNBD3_RID" ]; then + _dnbd3_client_additional_args=("--rid" "$SLX_DNBD3_RID") +fi + +if ! modprobe dnbd3; then + warn "Failed to load kernel module: dnbd3" +fi + +for try in {1..5} ""; do + if [ -z "$try" ]; then + emergency_shell "Failed to connect '${SLX_DNBD3_IMAGE}'" \ + "(revision: ${SLX_DNBD3_RID:-0})" \ + "from one of '$SLX_DNBD3_SERVERS' to '$_dnbd3_dev'." \ + "Check if the image exists on one of the servers" \ + "and if any is reachable from this client." + fi + info "Trying hosts '$SLX_DNBD3_SERVERS'." + if dnbd3-client \ + --host "$SLX_DNBD3_SERVERS" \ + --image "$SLX_DNBD3_IMAGE" \ + --device "$_dnbd3_dev" \ + "${_dnbd3_client_additional_args[@]}"; then + break + fi + sleep 1 +done + +# endregion +# region unpack dnbd3 image +if ! [[ $SLX_QCOW_HANDLER =~ ^(kernel|xloop|xmount)?$ ]]; then + emergency_shell "Unsupported QCOW handler: $SLX_QCOW_HANDLER" \ + "Use either 'xmount' or 'xloop'." +fi +if [ -z "$SLX_QCOW_HANDLER" ]; then + SLX_QCOW_HANDLER="xloop" + echo "SLX_QCOW_HANDLER='$SLX_QCOW_HANDLER'" >> /etc/openslx +fi +if [[ $SLX_QCOW_HANDLER =~ ^kernel|xloop$ ]]; then + read_only_device="$(container_unpack_xloop "$_dnbd3_dev")" +fi +if [ -z "$read_only_device" ]; then + read_only_device="$(container_unpack_xmount "$_dnbd3_dev")" +fi + +# Fail fast if unpacking dnbd3 image failed. +if [ -z "$read_only_device" ]; then + emergency_shell "Failed to unpack the qcow2 image!" +fi + +# endregion +# region find system partition within dnbd3 image +if [ -z "$SLX_SYSTEM_PARTITION_PREPARATION_SCRIPT" ]; then + # Find requested root partition - default to SLX_SYS label + read_only_partition="$( slx-tools dev_find_partitions \ + "$read_only_device" "${SLX_SYSTEM_PARTITION_IDENTIFIER:-SLX_SYS}" )" + if [ -z "$SLX_SYSTEM_PARTITION_IDENTIFIER" ]; then + if [ -n "$read_only_partition" ]; then + # Defaulting to SLX_SYS worked + echo "SLX_SYSTEM_PARTITION_IDENTIFIER='SLX_SYS'" >> /etc/openslx + else + # Aussume there is no partition table + read_only_partition="$read_only_device" + fi + fi + +else + eval "$SLX_SYSTEM_PARTITION_PREPARATION_SCRIPT" +fi +if [[ -z "$read_only_partition" ]]; then + warn "Failed to find unique device with identifier" \ + "\"${SLX_SYSTEM_PARTITION_IDENTIFIER}\"; matched devices:" \ + "\"${read_only_partition}\"" + exit 1 +fi +info "Using read-only partition: $read_only_partition" +# endregion + +# region add rw layer to dnbd3 image +# don't be fooled to think we are done, the next part is crucial +dmsetup-slx-device "$read_only_partition" +udevadm settle +# endregion diff --git a/modules.d/dnbd3-rootfs/hooks/s3-mount-root.sh b/modules.d/dnbd3-rootfs/hooks/s3-mount-root.sh new file mode 100755 index 00000000..11ea46e3 --- /dev/null +++ b/modules.d/dnbd3-rootfs/hooks/s3-mount-root.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +type emergency_shell >/dev/null 2>&1 || source /lib/dracut-lib.sh + +source "/etc/openslx" + +cnt=0 +while ! [ -b "$SLX_DNBD3_DEVICE_COW" ] \ + || ! [ "$( blockdev --getsize64 "$SLX_DNBD3_DEVICE_COW" 2> /dev/null )" -gt 0 ]; do + (( ++cnt > 20 )) && break + (( cnt % 5 == 0 )) && echo "Waiting for block device...." + usleep 100000 + (( cnt > 10 )) && udevadm trigger && udevadm settle +done + +ret=99 +if [ -n "$SLX_MOUNT_ROOT_OPTIONS" ]; then + # Always prefer mount options mandated by server + mount "$SLX_DNBD3_DEVICE_COW" "$NEWROOT" $SLX_MOUNT_ROOT_OPTIONS + ret=$? +else + # Let's guess it's ext4, tune for maximum performance as we don't care about recoverability + mount "$SLX_DNBD3_DEVICE_COW" "$NEWROOT" -o data=writeback,barrier=0,commit=60,noinit_itable,discard,noatime + ret=$? + if (( ret != 0 )); then + # Just try with no options and hope for the best... + mount "$SLX_DNBD3_DEVICE_COW" "$NEWROOT" + ret=$? + fi +fi + +(( ret != 0 )) && exit $ret + +if [ -n "$SLX_GENERATE_FSTAB_SCRIPT" ]; then + eval "$SLX_GENERATE_FSTAB_SCRIPT" +else + echo "" > "$NEWROOT/etc/fstab" +fi +true diff --git a/modules.d/dnbd3-rootfs/hooks/shutdown-umount.sh b/modules.d/dnbd3-rootfs/hooks/shutdown-umount.sh old mode 100644 new mode 100755 diff --git a/modules.d/dnbd3-rootfs/module-setup.sh b/modules.d/dnbd3-rootfs/module-setup.sh index eb5a4390..ce1b4b36 100755 --- a/modules.d/dnbd3-rootfs/module-setup.sh +++ b/modules.d/dnbd3-rootfs/module-setup.sh @@ -147,41 +147,49 @@ install() { dracut_module_included "network" && inst_hook cmdline 10 \ "$moddir/hooks/prepare-kernel-command-line-parameter.sh" inst_hook cmdline 90 "$moddir/hooks/set-dracut-environment-variables.sh" - inst_hook pre-udev 00 "$moddir/hooks/load-custom-kernel-modules.sh" - # Get the openslx config from the servers configured in the kernel command line. - inst_hook pre-mount 10 "$moddir/hooks/fetch-config.sh" # make the final blockdevice for the root system (dnbd3 -> xmount -> # device-mapper) - if dracut_module_included "systemd-initrd"; then - inst "$moddir/hooks/prepare-root-partition.sh" \ - /usr/local/bin/dnbd3root - inst_simple "${moddir}/services/dnbd3root.service" \ - "${systemdsystemunitdir}/dnbd3root.service" - mkdir --parents \ - "${initdir}/${systemdsystemunitdir}/dracut-mount.service.requires" - ln_r "${systemdsystemunitdir}/dnbd3root.service" \ - "${systemdsystemunitdir}/dracut-mount.service.requires/dnbd3root.service" - mkdir --parents \ - "${initdir}/${systemdsystemunitdir}/initrd.target.requires" - ln_r "${systemdsystemunitdir}/dnbd3root.service" \ - "${systemdsystemunitdir}/initrd.target.requires/dnbd3root.service" - # Copy systemd services to new root (so they don't get killed after - # switch_root) - inst_hook pre-pivot 00 \ - "$moddir/hooks/copy-dnbd3-files-into-newroot.sh" - inst_hook pre-pivot 00 \ - "$moddir/hooks/copy-dracut-systemd-files-into-newroot.sh" - inst_hook pre-shutdown 00 "$moddir/hooks/shutdown-umount.sh" - else - inst_hook pre-mount 10 "$moddir/hooks/prepare-root-partition.sh" - fi - inst_hook mount 10 "$moddir/hooks/mount-root-device.sh" - inst_hook pre-pivot 00 \ - "$moddir/hooks/copy-openslx-configuration-into-newroot.sh" + inst "$moddir/hooks/s3-dnbd3root.sh" \ + /usr/local/bin/s3-dnbd3root.sh + inst_simple "${moddir}/services/s3-dnbd3root.service" \ + "${systemdsystemunitdir}/s3-dnbd3root.service" + mkdir --parents \ + "${initdir}/${systemdsystemunitdir}/initrd-root-device.target.requires" + ln_r "${systemdsystemunitdir}/s3-dnbd3root.service" \ + "${systemdsystemunitdir}/initrd-root-device.target.requires/s3-dnbd3root.service" + # Mount the root file system + inst "$moddir/hooks/s3-mount-root.sh" \ + /usr/local/bin/s3-mount-root.sh + inst_simple "${moddir}/services/s3-mount-root.service" \ + "${systemdsystemunitdir}/s3-mount-root.service" + mkdir --parents \ + "${initdir}/${systemdsystemunitdir}/initrd-root-fs.target.requires" + ln_r "${systemdsystemunitdir}/s3-mount-root.service" \ + "${systemdsystemunitdir}/initrd-root-fs.target.requires/s3-mount-root.service" + # Copy /opt/openslx/config to newroot + inst "$moddir/hooks/s3-copy-openslx-config.sh" \ + /usr/local/bin/s3-copy-openslx-config.sh + inst_simple "${moddir}/services/s3-copy-openslx-config.service" \ + "${systemdsystemunitdir}/s3-copy-openslx-config.service" + mkdir --parents \ + "${initdir}/${systemdsystemunitdir}/initrd.target.requires" + ln_r "${systemdsystemunitdir}/s3-copy-openslx-config.service" \ + "${systemdsystemunitdir}/initrd.target.requires/s3-copy-openslx-config.service" + # Copy systemd services to new root (so they don't get killed after + # switch_root) + inst_hook pre-pivot 90 \ + "$moddir/hooks/copy-dracut-systemd-files-into-newroot.sh" + #inst_hook pre-shutdown 00 "$moddir/hooks/shutdown-umount.sh" + #inst_hook pre-pivot 00 \ + # "$moddir/hooks/copy-openslx-configuration-into-newroot.sh" # endregion # region scripts # endregion # region configuration files + mkdir --parents \ + "${initdir}/etc/systemd/system.conf.d" + inst "$moddir/configuration/systemd-newroot.conf" \ + "/etc/systemd/system.conf.d/systemd-newroot.conf" # Use terminal readline settings from the template system. inst /etc/inputrc /etc/inputrc # Set some aliases for the initramfs context. diff --git a/modules.d/dnbd3-rootfs/services/dnbd3root.service b/modules.d/dnbd3-rootfs/services/dnbd3root.service deleted file mode 100644 index df079576..00000000 --- a/modules.d/dnbd3-rootfs/services/dnbd3root.service +++ /dev/null @@ -1,12 +0,0 @@ -[Unit] -Description=root fs on dnbd3 (distributed network block device) -After=dracut-pre-mount.service network.target -Before=dracut-mount.service -DefaultDependencies=no -IgnoreOnIsolate=true - -[Service] -Type=oneshot -RemainAfterExit=true -KillMode=none -ExecStart=/usr/local/bin/dnbd3root diff --git a/modules.d/dnbd3-rootfs/services/s3-copy-openslx-config.service b/modules.d/dnbd3-rootfs/services/s3-copy-openslx-config.service new file mode 100644 index 00000000..41de7fe3 --- /dev/null +++ b/modules.d/dnbd3-rootfs/services/s3-copy-openslx-config.service @@ -0,0 +1,11 @@ +[Unit] +Description=Copy final /opt/openslx/config to /sysroot +Requires=initrd-root-fs.target +After=initrd-root-fs.target +DefaultDependencies=no +IgnoreOnIsolate=true + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/bin/s3-copy-openslx-config.sh diff --git a/modules.d/dnbd3-rootfs/services/s3-dnbd3root.service b/modules.d/dnbd3-rootfs/services/s3-dnbd3root.service new file mode 100644 index 00000000..3197a9cc --- /dev/null +++ b/modules.d/dnbd3-rootfs/services/s3-dnbd3root.service @@ -0,0 +1,15 @@ +[Unit] +Description=root fs on dnbd3 (distributed network block device) +Requires=s3-fetch-config.service +After=s3-fetch-config.service +After=dracut-pre-mount.service +Wants=dracut-mount.service +Before=dracut-mount.service +Before=initrd-root-device.target +DefaultDependencies=no +IgnoreOnIsolate=true + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/bin/s3-dnbd3root.sh diff --git a/modules.d/dnbd3-rootfs/services/s3-mount-root.service b/modules.d/dnbd3-rootfs/services/s3-mount-root.service new file mode 100644 index 00000000..626e21f4 --- /dev/null +++ b/modules.d/dnbd3-rootfs/services/s3-mount-root.service @@ -0,0 +1,12 @@ +[Unit] +Description=Mount dnbd3/devmapped device to /sysroot +Requires=initrd-root-device.target +After=initrd-root-device.target +Before=initrd-root-fs.target +DefaultDependencies=no +IgnoreOnIsolate=true + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/bin/s3-mount-root.sh diff --git a/modules.d/haveged/module-setup.sh b/modules.d/haveged/module-setup.sh old mode 100644 new mode 100755 diff --git a/modules.d/slx-addons/hooks/s3-setup-addons.sh b/modules.d/slx-addons/hooks/s3-setup-addons.sh new file mode 100755 index 00000000..0a947f23 --- /dev/null +++ b/modules.d/slx-addons/hooks/s3-setup-addons.sh @@ -0,0 +1,103 @@ +#!/usr/bin/env bash +# +# Stage4 addons setup script +# +############################################################################### +# Support stage4 addons residing under '/opt/openslx/addons'. +# This script will loop over all addons found, in the form of +# directories containing directory structures relative to '/' +# +# Additionally addons are expected to provide: +# * /addon-required -> script to check whether the +# addon should be installed (exit 0) or not (exit 1). +# +# * /opt/openslx/etc/.whiteout -> list of files +# that were removed during the addon installation and thus need to be +# removed after the addon was installed. +# +# * /opt/openslx/etc/ ld.so.cache -> ld cache containing +# the newly installed libraries. CAVE: multiple addons -> ld cache combination +# +### CURRENTLY NOT EXECUTED +# * /addon-init -> script that will be automatically +# be executed after the addon was installed. +# NOTE: question remains if this should be done within stage3 +# or by chroot'ing into the stage4 before executing it. +### CURRENTLY NOT EXECUTED: Since whiteouts could be handled for _all_addons + +type emergency_shell >/dev/null 2>&1 || . /lib/dracut-lib.sh + +# Check if we even have any addons to process +if [ ! -d "$NEWROOT/opt/openslx/addons" ]; then + info "No stage4 addons found." + return 0 +fi + +# This just activates the ldconfig service to run during the sysinit target +# Since addons are likely to install libs, this is necessary to garantee +# that the live system "sees" the libraries. +activate_stage4_ldconfig() { + local service_path="/opt/openslx/services/ldconfig-stage4.service" + [ -e "$service_path" ] || return 1 + local target_dir="${NEWROOT}/etc/systemd/system/sysinit.target.wants" + mkdir -p "$target_dir" + cp -f "$service_path" "${NEWROOT}/etc/systemd/system/${service_path##*/}" + ln -sf "../${service_path##*/}" "${target_dir}/${service_path##*/}" +} + +setup_addon() { + if [ ! -d "$1" ]; then + warn "Given '$1' not a directory, skipping." + return 1 + fi + local addon_dir="$1" + cd "$addon_dir" || return 1 + if ! bash addon-required 2>&1 ; then + info "'$addon_dir/addon-required' missing or returned non-zero, skipping..." + return 1 + fi + # purge addon-* files + rm -f -- addon-* + + # move all the files over + find . -not -type d 2>/dev/null | while read -r entry || [ -n "$entry" ]; do + entry="${entry#./}" + [ "$entry" != "${entry%/*}" ] && mkdir -p "${NEWROOT}/${entry%/*}" + mv -f -- "$entry" "${NEWROOT}/${entry}" + done + + # post merge: remove files marked as whiteouts + # (e.g. they were removed during the addon installation) + for WHITEOUT in "$NEWROOT/opt/openslx/etc/"*.whiteout; do + [ -e "$WHITEOUT" ] || continue + while read -r line; do + rm -f -- "${NEWROOT}/${line}" + done < "$WHITEOUT" + done + + cd - &>/dev/null +} + +active=() +for addon in "${NEWROOT}/opt/openslx/addons/"*; do + if setup_addon "$addon"; then + active+=("${addon#"${NEWROOT}/opt/openslx/addons/"}") + info "Activated '$addon' (@ $(date +%s))" + fi +done + +# if only one addon was installed, use its pre-generated ld.so.cache +# if more than one were installed, make sure ldconfig is called in stage4 +if [ "${#active[@]}" -eq 1 ]; then + addon_cache="${NEWROOT}/opt/openslx/etc/${active[0]}.ld.so.cache" + if [ -e "$addon_cache" ]; then + info "Using ld.so.cache of '${active[0]}'." + mv -f -- "${NEWROOT}/etc/ld.so.cache"{,.stage4} + mv -f -- "$addon_cache" "${NEWROOT}/etc/ld.so.cache" + fi +elif [ "${#active[@]}" -gt 1 ]; then + info "Activating ldconfig in stage4 due to multiple loaded addons: ${active[*]}" + activate_stage4_ldconfig +fi + +: diff --git a/modules.d/slx-addons/module-setup.sh b/modules.d/slx-addons/module-setup.sh index 7a2d78aa..5d84584c 100755 --- a/modules.d/slx-addons/module-setup.sh +++ b/modules.d/slx-addons/module-setup.sh @@ -8,7 +8,16 @@ depends() { echo dnbd3-rootfs } install() { - inst_hook pre-pivot 70 "$moddir/scripts/setup-addons.sh" - inst "${moddir}/services/ldconfig.service" \ - "/opt/openslx/services/ldconfig.service" + inst "${moddir}/services/ldconfig-stage4.service" \ + "/opt/openslx/services/ldconfig-stage4.service" + # Install service that enables addons + _name="s3-setup-addons" + inst "$moddir/hooks/${_name}.sh" \ + "/usr/local/bin/${_name}.sh" + inst_simple "${moddir}/services/${_name}.service" \ + "${systemdsystemunitdir}/${_name}.service" + mkdir --parents \ + "${initdir}/${systemdsystemunitdir}/dracut-pre-pivot.service.requires" + ln_r "${systemdsystemunitdir}/${_name}.service" \ + "${systemdsystemunitdir}/dracut-pre-pivot.service.requires/${_name}.service" } diff --git a/modules.d/slx-addons/scripts/setup-addons.sh b/modules.d/slx-addons/scripts/setup-addons.sh deleted file mode 100644 index d4b94a1a..00000000 --- a/modules.d/slx-addons/scripts/setup-addons.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env bash -# -# Stage4 addons setup script -# -############################################################################### -# Support stage4 addons residing under '/opt/openslx/addons'. -# This script will loop over all addons found, in the form of -# directories containing directory structures relative to '/' -# -# Additionally addons are expected to provide: -# * /addon-required -> script to check whether the -# addon should be installed (exit 0) or not (exit 1). -# -# * /opt/openslx/etc/.whiteout -> list of files -# that were removed during the addon installation and thus need to be -# removed after the addon was installed. -# -# * /opt/openslx/etc/ ld.so.cache -> ld cache containing -# the newly installed libraries. CAVE: multiple addons -> ld cache combination -# -### CURRENTLY NOT EXECUTED -# * /addon-init -> script that will be automatically -# be executed after the addon was installed. -# NOTE: question remains if this should be done within stage3 -# or by chroot'ing into the stage4 before executing it. -### CURRENTLY NOT EXECUTED: Since whiteouts could be handled for _all_addons - -type emergency_shell >/dev/null 2>&1 || . /lib/dracut-lib.sh - -# Check if we even have any addons to process -if [ ! -d "$NEWROOT/opt/openslx/addons" ]; then - info "No stage4 addons found." - return 0 -fi - -# This just activates the ldconfig service to run during the sysinit target -# Since addons are likely to install libs, this is necessary to garantee -# that the live system "sees" the libraries. -activate_stage4_ldconfig() { - local service_path="/opt/openslx/services/ldconfig.service" - [ -e "$service_path" ] || return 1 - local target_dir="${NEWROOT}/etc/systemd/system/sysinit.target.wants" - mkdir -p "$target_dir" - cp -f "$service_path" "${NEWROOT}/etc/systemd/system/${service_path##*/}" - ln -sf "../${service_path##*/}" "${target_dir}/${service_path##*/}" -} - -setup_addon() { - if [ ! -d "$1" ]; then - warn "Given '$1' not a directory, skipping." - return 1 - fi - local addon_dir="$1" - cd "$addon_dir" - if ! bash addon-required 2>&1 ; then - info "'$addon_dir/addon-required' missing or returned non-zero, skipping..." - return 1 - fi - # purge addon-* files - rm -f -- addon-* - - # move all the files over - for entry in $(find * -not -type d 2>/dev/null); do - [ "$entry" != "${entry%/*}" ] && mkdir -p "${NEWROOT}/${entry%/*}" - mv -f -- "$entry" "${NEWROOT}/${entry}" - done - - # post merge: remove files marked as whiteouts - # (e.g. they were removed during the addon installation) - for WHITEOUT in "$NEWROOT/opt/openslx/etc/"*.whiteout; do - [ -e "$WHITEOUT" ] || continue - while read line; do - rm -f "${NEWROOT}/${line}" - done < "$WHITEOUT" - done - - cd - &>/dev/null -} - -active=() -for addon in "${NEWROOT}/opt/openslx/addons/"*; do - if setup_addon "$addon"; then - active+=("${addon#${NEWROOT}/opt/openslx/addons/}") - info "Activated '$addon' (@ $(date +%s))" - fi -done - -# if only one addon was installed, use its pre-generated ld.so.cache -# if more than one were installed, make sure ldconfig is called in stage4 -if [ "${#active[@]}" -eq 1 ]; then - addon_cache="${NEWROOT}/opt/openslx/etc/${active[0]}.ld.so.cache" - if [ -e "$addon_cache" ]; then - info "Using ld.so.cache of '${active[0]}'." - mv -f -- "${NEWROOT}/etc/ld.so.cache"{,.stage4} - mv -f -- "$addon_cache" "${NEWROOT}/etc/ld.so.cache" - fi -elif [ "${#active[@]}" -gt 1 ]; then - info "Activating ldconfig in stage4 due to multiple loaded addons: ${active[@]}" - activate_stage4_ldconfig -fi - -: diff --git a/modules.d/slx-addons/services/ldconfig-stage4.service b/modules.d/slx-addons/services/ldconfig-stage4.service new file mode 100644 index 00000000..be799e66 --- /dev/null +++ b/modules.d/slx-addons/services/ldconfig-stage4.service @@ -0,0 +1,8 @@ +[Unit] +Description=Run ldconfig +Before=graphical.target +DefaultDependencies=no + +[Service] +Type=oneshot +ExecStart=/sbin/ldconfig diff --git a/modules.d/slx-addons/services/ldconfig.service b/modules.d/slx-addons/services/ldconfig.service deleted file mode 100644 index be799e66..00000000 --- a/modules.d/slx-addons/services/ldconfig.service +++ /dev/null @@ -1,8 +0,0 @@ -[Unit] -Description=Run ldconfig -Before=graphical.target -DefaultDependencies=no - -[Service] -Type=oneshot -ExecStart=/sbin/ldconfig diff --git a/modules.d/slx-addons/services/s3-setup-addons.service b/modules.d/slx-addons/services/s3-setup-addons.service new file mode 100644 index 00000000..ee6c203b --- /dev/null +++ b/modules.d/slx-addons/services/s3-setup-addons.service @@ -0,0 +1,11 @@ +[Unit] +Description=Enable SLX addons +After=initrd-root-fs.target +Before=initrd-switch-root.target +DefaultDependencies=no +IgnoreOnIsolate=true + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/bin/s3-setup-addons.sh diff --git a/modules.d/slx-clock/hooks/s3-configure-timesyncd.sh b/modules.d/slx-clock/hooks/s3-configure-timesyncd.sh new file mode 100755 index 00000000..77541ab0 --- /dev/null +++ b/modules.d/slx-clock/hooks/s3-configure-timesyncd.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +. /etc/openslx + +if [ -n "$SLX_NTP_SERVER" ]; then + tsdir="${NEWROOT}/etc/systemd/timesyncd.conf.d" + mkdir -p "$tsdir" + { + echo "[Time]" + echo "NTP=$SLX_NTP_SERVER" + } > "${tsdir}/slx-ntp.conf" +fi diff --git a/modules.d/slx-clock/hooks/s3-ntp-sync.sh b/modules.d/slx-clock/hooks/s3-ntp-sync.sh new file mode 100755 index 00000000..96e72c04 --- /dev/null +++ b/modules.d/slx-clock/hooks/s3-ntp-sync.sh @@ -0,0 +1,53 @@ +#!/bin/ash +# Sync time via network + +. /etc/openslx + +ntp_sync() { + # Try to merge with servers from DHCP. Prefer DHCP, skip duplicates + for SERVER in $SLX_NTP_SERVER; do + if ! echo "$SLX_DHCP_NTP" | grep -wq "$SERVER"; then + SLX_DHCP_NTP="$SLX_DHCP_NTP $SERVER" + fi + done + local SUCCESS= + for SERVER in $SLX_DHCP_NTP; do + if timeout 3 ntpd -q -n -p "$SERVER"; then + echo "Successfully queried $SERVER for time." + if [ "x$SLX_BIOS_CLOCK" = "xlocal" ]; then + usleep 100000 + hwclock -l -w || echo "... but could not set BIOS clock to localtime" + elif [ "x$SLX_BIOS_CLOCK" = "xutc" ]; then + usleep 100000 + hwclock -u -w || echo "... but could not set BIOS clock to UTC" + fi + SUCCESS=1 + break + fi + echo "Error querying $SERVER for current time." + done + if [ -z "$SUCCESS" ]; then + echo "No NTP server reachable" + # See if we have a timestamp in our server-config - should only be a few seconds off by now + if [ -n "$SLX_NOW" ] && [ "$SLX_NOW" -gt 1234567890 ]; then + TTS="$SLX_NOW" + if [ -n "$CONFIG_DOWNLOAD_TIME" ]; then + NOW_TIME=$(sed -r 's/^([0-9]+)\.([0-9]+).*$/\1\2/' /proc/uptime) + if [ "$CONFIG_DOWNLOAD_TIME" -gt 0 ] && [ "$NOW_TIME" -gt 0 ]; then + TTS=$(( TTS + ( NOW_TIME - CONFIG_DOWNLOAD_TIME ) / 100 )) + fi + fi + echo "Setting time to SLX_NOW ($SLX_NOW, corrected $TTS)" + date -s "@$TTS" + else + echo "No fallback option for timesync available, relying on correct RTC setup" + if [ "x$SLX_BIOS_CLOCK" = "xlocal" ]; then + # Linux defaults to RTC = UTC, so read again in this case + hwclock -l -s + fi + fi + fi + echo "Timesync finished" +} + +ntp_sync diff --git a/modules.d/slx-clock/module-setup.sh b/modules.d/slx-clock/module-setup.sh index f9ff0c48..3b62089c 100755 --- a/modules.d/slx-clock/module-setup.sh +++ b/modules.d/slx-clock/module-setup.sh @@ -8,16 +8,16 @@ depends() { echo dnbd3-rootfs busybox } install() { - # see where systemd-cat is installed - local sdc_bin="$( command -v systemd-cat )" - if [ -z "$sdc_bin" ]; then - warn "Could not find systemd-cat in $PATH. Check if its installed." - return 1 - fi - # wait til we have the openslx config for ntp servers - # which happens in pre-mount/10 - inst_multiple "/etc/services" "/usr/share/zoneinfo/Europe/Berlin" "$sdc_bin" + inst_multiple "/etc/services" "/usr/share/zoneinfo/Europe/Berlin" inst "/usr/share/zoneinfo/Europe/Berlin" "/etc/localtime" - inst_hook pre-mount 15 "$moddir/scripts/ntp-sync.sh" - inst_hook pre-pivot 15 "$moddir/scripts/configure-timesyncd.sh" + for _name in "s3-setup-addons" "s3-configure-timesyncd"; do + inst "$moddir/hooks/${_name}.sh" \ + "/usr/local/bin/${_name}.sh" + inst_simple "${moddir}/services/${_name}.service" \ + "${systemdsystemunitdir}/${_name}.service" + mkdir --parents \ + "${initdir}/${systemdsystemunitdir}/dracut-pre-pivot.service.requires" + ln_r "${systemdsystemunitdir}/${_name}.service" \ + "${systemdsystemunitdir}/dracut-pre-pivot.service.requires/${_name}.service" + done } diff --git a/modules.d/slx-clock/scripts/configure-timesyncd.sh b/modules.d/slx-clock/scripts/configure-timesyncd.sh deleted file mode 100644 index 77541ab0..00000000 --- a/modules.d/slx-clock/scripts/configure-timesyncd.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -. /etc/openslx - -if [ -n "$SLX_NTP_SERVER" ]; then - tsdir="${NEWROOT}/etc/systemd/timesyncd.conf.d" - mkdir -p "$tsdir" - { - echo "[Time]" - echo "NTP=$SLX_NTP_SERVER" - } > "${tsdir}/slx-ntp.conf" -fi diff --git a/modules.d/slx-clock/scripts/ntp-sync.sh b/modules.d/slx-clock/scripts/ntp-sync.sh deleted file mode 100755 index a533353c..00000000 --- a/modules.d/slx-clock/scripts/ntp-sync.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/ash -# Sync time via network - -. /etc/openslx - -ntp_sync() { - # Try to merge with servers from DHCP. Prefer DHCP, skip duplicates - for SERVER in $SLX_NTP_SERVER; do - if ! echo "$SLX_DHCP_NTP" | grep -wq "$SERVER"; then - SLX_DHCP_NTP="$SLX_DHCP_NTP $SERVER" - fi - done - local SUCCESS= - for SERVER in $SLX_DHCP_NTP; do - if timeout 3 ntpd -q -n -p "$SERVER"; then - echo "Successfully queried $SERVER for time." - if [ "x$SLX_BIOS_CLOCK" = "xlocal" ]; then - usleep 100000 - hwclock -l -w || echo "... but could not set BIOS clock to localtime" - elif [ "x$SLX_BIOS_CLOCK" = "xutc" ]; then - usleep 100000 - hwclock -u -w || echo "... but could not set BIOS clock to UTC" - fi - SUCCESS=1 - break - fi - echo "Error querying $SERVER for current time." - done - if [ -z "$SUCCESS" ]; then - echo "No NTP server reachable" - # See if we have a timestamp in our server-config - should only be a few seconds off by now - if [ -n "$SLX_NOW" ] && [ "$SLX_NOW" -gt 1234567890 ]; then - TTS="$SLX_NOW" - if [ -n "$CONFIG_DOWNLOAD_TIME" ]; then - NOW_TIME=$(sed -r 's/^([0-9]+)\.([0-9]+).*$/\1\2/' /proc/uptime) - if [ "$CONFIG_DOWNLOAD_TIME" -gt 0 ] && [ "$NOW_TIME" -gt 0 ]; then - TTS=$(( TTS + ( NOW_TIME - CONFIG_DOWNLOAD_TIME ) / 100 )) - fi - fi - echo "Setting time to SLX_NOW ($SLX_NOW, corrected $TTS)" - date -s "@$TTS" - else - echo "No fallback option for timesync available, relying on correct RTC setup" - if [ "x$SLX_BIOS_CLOCK" = "xlocal" ]; then - # Linux defaults to RTC = UTC, so read again in this case - hwclock -l -s - fi - fi - fi - echo "Timesync finished" -} - -if command -v systemd-cat > /dev/null; then - ntp_sync 2>&1 | systemd-cat -else - ntp_sync -fi - -true - diff --git a/modules.d/slx-clock/services/s3-configure-timesyncd.service b/modules.d/slx-clock/services/s3-configure-timesyncd.service new file mode 100644 index 00000000..c370859a --- /dev/null +++ b/modules.d/slx-clock/services/s3-configure-timesyncd.service @@ -0,0 +1,12 @@ +[Unit] +Description=Configure timesyncd in stage 4 +After=initrd-root-fs.target +After=s3-ntp-sync.service +Before=initrd-switch-root.target +DefaultDependencies=no +IgnoreOnIsolate=true + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/bin/s3-configure-timesyncd.sh diff --git a/modules.d/slx-clock/services/s3-ntp-sync.service b/modules.d/slx-clock/services/s3-ntp-sync.service new file mode 100644 index 00000000..77208d0f --- /dev/null +++ b/modules.d/slx-clock/services/s3-ntp-sync.service @@ -0,0 +1,11 @@ +[Unit] +Description=Sync time via NTP +Requires=s3-setup-bootif-network.service +After=s3-setup-bootif-network.service +DefaultDependencies=no +IgnoreOnIsolate=true + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/bin/s3-ntp-sync.sh diff --git a/modules.d/slx-dmsetup/hooks/dmsetup-slx-device b/modules.d/slx-dmsetup/hooks/dmsetup-slx-device new file mode 100755 index 00000000..77e51c7d --- /dev/null +++ b/modules.d/slx-dmsetup/hooks/dmsetup-slx-device @@ -0,0 +1,794 @@ +#!/usr/bin/env bash +# +# Script to back given read-only device using the block device +# specified by SLX_WRITABLE_DEVICE_IDENTIFIER in the SLX config. +# If SLX_WRITABLE_DEVICE_PARTITION_TABLE is sepcified, it will +# further create device mapper devices accordingly. +# +# Example partition config: +# +# thin-snapshot root 10G 1 +# thin-volume tmp 20G 0 +# linear data0 5-10G 1 +# linear data1 1-50% 1 +# +# NOTE: Encrypting thin-snapshot will actually encrypt the +# entire pool data device used for the pool. +# TODO: Support external keys +# TODO: Put table in file in config.tgz + +type -p emergency_shell || . /lib/dracut-lib.sh + +# for debugging purposes +exec {BASH_XTRACEFD}> /run/openslx/dmsetup.log +set -x + +# read-only device to prepare for CoW +[ -z "$1" ] && emergency_shell "Read-only device was not given!" +declare -g read_only_device="$1" +declare -g read_only_device_sz="$( blockdev --getsz "$1" )" +# Use _sz suffix for sizes expressed in number of 512b sectors, +# _size for random other crap + +declare -rg ntfs_list="/run/openslx/.thin-ntfs-candidates" + +# handle_unit +# Supply percentage, or size in [kmgt]bytes, +# returns appropriate value in number of 512b sectors +handle_unit() { + # default to bytes + local -i potency=0 + local -i val="$1" + case "$2" in + [%]) # These are relative to the writable CoW device + # Allow > 100% for over-provisioning + val="$(( remaining_device_sz * val / 100 ))" + ;; + [Kk]) potency=1 ;;& + [Mm]) potency=2 ;;& + [Gg]) potency=3 ;;& + [Tt]) potency=4 ;;& + *) + # => 1024 ** potency for G, M, K, etc results in bytes + # => bytes / 512 = sectors + val=$(( val * ( 1024 ** potency) / 512 )) + ;; + esac + echo "$val" +} + +parse_config() { + local remaining_device_sz="$writable_device_sz" + parse_config_int "$1" 0 + parse_config_int "$1" 1 +} + +# global array variables storing the configuration of the partitions +declare -ag linear snapshot thin_snapshot thin_volume +parse_config_int() { + [ -z "$1" ] && return 1 + local -i rel_only="$2" + while IFS= read -r line || [ -n "$line" ]; do + [ -z "$line" ] && continue + read -r type name range crypt ignore <<< "$line" + type=${type//-/_} # to use the type as variable in eval + if ! [[ "$type" =~ \ + ^(linear|snapshot|thin_snapshot|thin_volume)$ ]]; then + echo "$0: Ignoring invalid type: $line" + continue + fi + if [[ -z "$name" ]]; then + echo "$0: Ignoring nameless entry: $line" + continue + fi + unset min_unit max_unit min max + # ranges can be like: 40G, 40-80G, 10G-20% + if ! [[ "$range" =~ ^([0-9]+)([GgMmKkBb%]?)(-([0-9]+)([GgMmKkBb%]?))?$ ]]; then + echo "$0: Ignoring invalid range: $line" + continue + fi + local min="${BASH_REMATCH[1]}" + local max="${BASH_REMATCH[4]:-${BASH_REMATCH[1]}}" + local min_unit="${BASH_REMATCH[2]:-${BASH_REMATCH[5]}}" + local max_unit="${BASH_REMATCH[5]:-${BASH_REMATCH[2]}}" + # first pass we handle absolute values unly, second pass relative ones + if [[ "$min_unit" = "%" || "$max_unit" = "%" ]]; then + [ "$rel_only" != 1 ] && continue + else + [ "$rel_only" = 1 ] && continue + fi + if [ -z "$min_unit" ]; then + echo "$0: WARNING: No unit given in range, assuming BYTES: $line" + fi + min="$( handle_unit "$min" "$min_unit" )" + max="$( handle_unit "$max" "$max_unit" )" + if (( min > max )); then + # So, we might end up with something like 30G-100%, but the writable device + # is only 20GB. In that case we most likely want to contine, and not consider + # this an error. So let's try to come up with some logic on what is an error + # and what isn't. Probably anything involving a mix of percentage and + # non-percentage should not be an error. + if [[ "$min_unit" = "%" && "$max_unit" != "%" ]] \ + || [[ "$min_unit" != "%" && "$max_unit" = "%" ]]; then + # Let's hope for the best + max="$min" + else + echo "$0: Ignoring invalid range (min > max): $line" + continue + fi + fi + if ! [[ "$crypt" =~ ^[01]$ ]]; then + echo "$0: Disabling encryption due to invalid crypt argument: $line" + crypt=0 + fi + # finally save it to the global array for this type + case "$type" in + linear) linear+=("${name} ${crypt} ${min} ${max}") ;; + snapshot) snapshot+=("${name} ${crypt} ${min} ${max}") ;; + thin_snapshot) thin_snapshot+=("${name} ${crypt} ${min} ${max}") ;; + thin_volume) thin_volume+=("${name} ${crypt} ${min} ${max}") ;; + *) echo "$0: SOMETHING NOT GOOT CHECK SOURCE CODE" ;; + esac + # Decrease for upcoming calculations if we used fixed values here + if [ "$rel_only" != 1 ]; then + (( remaining_device_sz -= ( min + max ) / 2 )) + fi + done <<< "$1" +} + +# Helper to call 'dmsetup setup' without syncing with udev +# and then actively create the devices with the mknodes command. +# Either pass the table contents as $2, or pipe them into the function +# dmsetup_create_noudevsync [table] +dmsetup_create_noudevsync() { + ( + set -eo pipefail + if [ -n "$2" ]; then + printf "%s\n" "$2" | dmsetup create "$1" --noudevsync + else + dmsetup create "$1" --noudevsync + fi + dmsetup mknodes --noudevsync "$1" + ) + local ret=$? + [ -b "/dev/mapper/$1" ] || ret=99 + [ $ret -ne 0 ] && dmsetup remove --noudevsync "$1" + return $ret +} + +# encrypt_device [] +encrypt_device() { + # TODO: Send key back to us, demand ransom + modprobe dm-crypt || echo "$0: dm-crypt loading failed, maybe builtin?" + [ -b "$1" ] || return 1 + [ -n "$2" ] || return 1 + [ -z "$3" ] && local size="$( blockdev --getsz "$1" )" + local key + key="$( < /dev/urandom xxd -c32 -p -l32 )" + [ -z "$key" ] && key="$( < /dev/urandom tr -c -d 'a-f0-9' | dd count=1 bs=32 )" + [ -z "$key" ] && key="$( < /dev/urandom head -c32 | xxd -c32 -p )" + [ -z "$key" ] && key="$( < /dev/urandom xxd -c32 -p | head -n 1 )" + [ -z "$key" ] && echo "$0: ERROR: Could not generate encryption key" + if ! dmsetup_create_noudevsync "$2" \ + "0 ${3:-${size}} crypt aes-xts-plain64 $key 0 $1 0 1 allow_discards"; then + echo "$0: Failed to encrypt $1." + return 1 + fi + return 0 +} +# create_snapshot " " +create_snapshot() { + modprobe dm-snapshot || echo "$0: dm-snapshot loading failed, maybe builtin?" + read -r name persist ignore <<< "$1" + if ! dmsetup_create_noudevsync "$name" \ + "0 $read_only_device_sz snapshot $read_only_device $writable_device ${persist:-N} 8"; then + echo "$0: Failed to create snapshot on '$writable_device' for '$read_only_device'." + return 1 + fi + return 0 +} + +# This function is called if no ID44 partition could be found or anoother kind +# of critical error occurs during the CoW layer setup. It will combine the +# the read-only device with a DM zero device to increase its virtual size +# by half the RAM size. A sparse file of that size will then be created and +# placed on a dedicated tmpfs. +# THIS FUNCTION MUST NEVER RETURN +ramdisk_fallback() { + echo "$0: Falling back to regular dm-snapshot on a RAMdisk." + + # RAM size in kb, note that this is equal to half + # of the entire RAM when interpreted as 512-bytes sectors. + local ram_cow_sz="$(awk '/^MemTotal:/ { printf("%d\n", $2 ); exit }' /proc/meminfo)" + + # try to prepare the zero extension device + local extended_device="/dev/mapper/${read_only_device##*/}-extended" + ( + set -e + lsmod | grep -q dm-zero || modprobe dm-zero + dmsetup_create_noudevsync "${extended_device##*/}" \ + "0 $read_only_device_sz linear $read_only_device 0 + $read_only_device_sz $ram_cow_sz zero" + ) + local ret="$?" + if [ "$ret" -eq 0 ]; then + read_only_device="$extended_device" + read_only_device_sz="$(( read_only_device_sz + ram_cow_sz ))" + else + echo "$0: Failed to setup the fake larger '$read_only_device'." + echo "$0: Continuing with its original size." + fi + + # prepare dedicated tmpfs mount point + local cow_tmpfs="/run/openslx/cow" + if ! mkdir -p "$cow_tmpfs"; then + cow_tmpfs="${cow_tmpfs}.$$.$RANDOM" + mkdir -p "$cow_tmpfs" + fi + if ! mount -t tmpfs cow-tmpfs -o size="$(( read_only_device_sz / 2 + 100 ))k" "$cow_tmpfs"; then + echo "$0: Failed to mount tmpfs in '$cow_tmpfs' of size '$(( read_only_device_sz / 2 + 100 ))KiB'." + fi + + # create sparse file there + local file="$(mktemp -u -p "$cow_tmpfs" dnbd_cow.XXX)" + if ! dd if=/dev/null of="$file" seek="$(( read_only_device_sz ))" bs=512 2> /dev/null; then + emergency_shell "Failed to allocate CoW file $file." + fi + declare -rg writable_device="$(losetup --show --find "$file")" + local cow_device_candidate="root" + while [ -b "/dev/mapper/$cow_device_candidate" ]; do + cow_device_candidate="root.$RANDOM" + done + if [ -z "$writable_device" ] || ! create_snapshot "$cow_device_candidate N"; then + emergency_shell "CRITICAL: failed to setup RAMdisk fallback." + exit 1 + fi + finish_setup "$cow_device_candidate" "0" "$read_only_device_sz" +} + +# finish_setup [] +# is the device name only, /dev/mapper will be prepended automatically. +# denotes if the created device lies in a RAMdisk (0) or is backed by a disk (1). +# is given in sectors. +# THIS FUNCTION MUST NEVER RETURN +finish_setup() { + if [ -z "$1" ] || ! [ -b "/dev/mapper/$1" ]; then + emergency_shell "'/dev/mapper/$1' not a block device. Failed to setup CoW layer." + exit 1 + fi + if ! [[ "$2" =~ ^[0-9]$ ]]; then + emergency_shell "'$2' not a valid type, 0 or 1 expected." + fi + # optional? + { + echo "# Generated by '$0'." + echo "SLX_DNBD3_DEVICE_COW=/dev/mapper/$1" + } >> /etc/openslx + save_partition_info "$1" "/" "$2" "$3" + exit 0 +} + +# path to save the achieved setup to +declare -rg partitions_config="/run/openslx/dmsetup.state" +cat <<-EOF > "$partitions_config" +# Generated by '$0'. +# Format: +# Options can be: +# * type -> CoW layer type: 0 is RAMdisk, 1 is disk, 2 is network +# * size -> in 512 byte sectors +EOF + +# save_partition_info [] +save_partition_info() { + [ -b "/dev/mapper/$1" ] || return 1 + [ -n "$2" ] || return 1 + [[ "$3" =~ ^[0-9]$ ]] || return 1 + local opts="type=$3" + # plain size given + [[ "$4" =~ ^[0-9]+$ ]] && opts="$opts,physical_size=$4" + # - + [[ "$4" =~ ^[0-9]+-[0-9]+$ ]] && opts="$opts,shared_physical_size=${4%-*},virtual_size=${4#*-}" + echo "/dev/mapper/$1 $2 ${opts}" >> "$partitions_config" +} + +# This will create another dm-linear on top of $scratch_device in case its +# size differs from $scratch_device_sz. This is useful for setups where you +# cannot explicitly configure how much space to use from the underlying device, +# and the partition table says not to use the entire $writable_device for cow +require_exact_scratch_size() { + local current_sz="$( blockdev --getsz "$scratch_device" )" + (( current_sz == scratch_device_sz )) && return 0 # Everything fine + if (( current_sz < scratch_device_sz )); then + echo "$0: WARNING: scratch_device_sz is larger than actual device." + echo "$0: This should never happen." + scratch_device_sz="$current_sz" + return 0 + fi + # We could check if $scratch_device already is a dm target, and just adjust its + # size, but I think that scenario isn't possible, currently. + if ! dmsetup_create_noudevsync "scratch" "0 $scratch_device_sz linear $scratch_device 0"; then + echo "$0: Failed to create scratch space for the CoW layer." + return 1 + fi + scratch_device="/dev/mapper/scratch" + save_partition_info "scratch" "*" "1" "$scratch_device_sz" + return 0 +} + +create_pool() { + declare -r data_block_sz=256 # Desired Block size (number of 512byte sectors) + declare -r wanted_low_mb=100 # Free space below this will trigger a dm event + # create external snapshot for read-only device + # create remaining thin volumes + modprobe dm-thin-pool || echo "$0: dm-thin-pool load failed, maybe builtin?" + # create temporary metadata device + # calculate number of sectors needed and check boundaries: + # XXX Formula from thin-pool.txt calculates size in *bytes*, we want 512b blocks + metadata_dev_sz="$(( 48 * scratch_device_sz / data_block_sz / 512 ))" + # If we want NTFS as a backup plan to extend the pool, check if the current size + # is less than 100GB, and only then consider this feature. + # Maybe make that thresold configurable one day, but the the desktop client + # use case this is sensible for now. + if [ "$SLX_NTFSFREE" = "backup" ] && (( scratch_device_sz < 209715200 )) \ + && [ -z "$metadata_persistent" ]; then + find_ntfs_partitions + if [ -s "$ntfs_list" ]; then + # Look what size we end up if we want at least 50GB + local sum="$( awk -v sum=0 \ + '{sum+=$1; if (sum >= 104857600) exit}END{printf "%.0f", sum}' \ + "$ntfs_list" )" + if (( sum > 0 )); then + (( sum > 209715200 )) && sum=209715200 # Max 100GB + # Account for this potential growth in the metadata device size for future expansion + metadata_dev_sz="$(( metadata_dev_sz + 48 * sum / data_block_sz / 512 ))" + echo "$sum" > "/run/openslx/.thin-ntfs-growsize" + root_ntfs_extra="$sum" + fi + fi + fi + # Min 2MB -> 4096 sectors, max 16GB -> 33554432 sectors + [ "$metadata_dev_sz" -lt 4096 ] && metadata_dev_sz="4096" + # TODO handle the exotic case of a too large metadata device to fit within RAM. + [ "$metadata_dev_sz" -gt 33554432 ] && metadata_dev_sz="33554432" + local scratch_device_offset=0 + local metadata_dev= + local metadata_persistent= + if [ -n "$metadata_persistent" ]; then + # create persistent slice of the writable device for the pool metadata + if ! dmsetup_create_noudevsync "pool-metadata" \ + "0 $metadata_dev_sz linear $scratch_device $scratch_device_offset"; then + echo "$0: Failed to create linear device for pool metadata device." + else + # Adjust size for pool-data down accordingly + scratch_device_offset="$metadata_dev_sz" + scratch_device_sz=$(( scratch_device_sz - metadata_dev_sz )) + declare -r metadata_dev="/dev/mapper/pool-metadata" + # TODO configurable wipe: dd if=/dev/zero of="$metadata_dev" count=1 bs=4096 + # TODO: If we fail later on in this function, we would actually have to destroy + # this target again, and re-adjust the offset and size back, so that the + # snapshot fallback would work properly. Or maybe just don't support fallback. + fi + fi + if [ -z "$metadata_dev" ]; then + # create RAMdisk in /run for metadata device + metadata_dev="$(mktemp -p /run/openslx .pool-metadata.XXX)" + # Create sparse file of required size + dd if=/dev/null of="$metadata_dev" bs=512 seek="$metadata_dev_sz" 2> /dev/null + declare -r metadata_dev="$( losetup --show --find "$metadata_dev" )" + fi + if [ -z "$metadata_dev" ]; then + echo "$0: Could not set up persistent or tmpfs-loop metadata device. Aborting." + return 1 + fi + + local pool_data_dev + if (( root_ntfs_extra == 0 )) && (( scratch_device_offset == 0 )); then + # No offset, no potential expansion, don't create another linear target + pool_data_dev="$scratch_device" + else + pool_data_dev="/dev/mapper/pool-data" + # Create linear device of the writable device, in case we have an offset from + # the on-disk meta data. Also this way we can easily extend it later. + if ! dmsetup_create_noudevsync "${pool_data_dev##*/}" \ + "0 $scratch_device_sz linear $scratch_device $scratch_device_offset"; then + echo "$0: Failed to create pool data device on '$scratch_device'." + return 1 + fi + fi + local low_water_mark + # Convert MB to blocks + low_water_mark=$(( wanted_low_mb * 2048 / data_block_sz )) + if ! dmsetup_create_noudevsync "${pool_dev##*/}" \ + "0 $scratch_device_sz thin-pool $metadata_dev $pool_data_dev $data_block_sz $low_water_mark 1 skip_block_zeroing"; then + echo "$0: Failed to create thin-pool device (meta: $metadata_dev, data: $pool_data_dev)" + return 1 + fi + return 0 +} + +# create_volume [backing_dev] +create_volume() { + if [ -z "$pool_dev" ] || ! [ -b "$pool_dev" ]; then + echo "$0: Global pool device not set or present." + return 1 + fi + if [ $# -lt 3 ] || [ -z "$1" ]; then + echo "$0: create_volume: not enough arguments." + return 1 + fi + local name="$1" + local id="$2" + local size="$3" + local backing_dev="$4" # Optional, internal if empty + + if ! dmsetup message "$pool_dev" 0 "create_thin $id"; then + echo "$0: Failed to create thin volume with id '$id' in pool '$pool_dev'." + echo "$0: It might already exists, trying anyway..." + fi + if ! dmsetup_create_noudevsync "$name" "0 $size thin $pool_dev $id $backing_dev"; then + echo "$0: Failed to create external snapshot named '$name':" + echo " Size: $size" + echo " Backing device: $backing_dev" + echo " Thin volume id: $id" + return 1 + fi + return 0 +} + +# Find NTFS partitions with decently sized ranges of +# free space. We can use these as our writable layer +# for our thin-pool, if configured. +# If suitable, this will create the file $ntfs_list with +# one line per suitable partition, format +# total_size_blocks devpath +# Results are sorted by size, descending order +find_ntfs_partitions() { + [ -z "$SLX_NTFSFREE" ] && return + [ "$SLX_NTFSFREE" = "never" ] && return + [ -e "$ntfs_list" ] && return + if ! command -v ntfsfree &> /dev/null; then + echo "$0: ntfsfree not found, cannot use NTFS partitions as RW layer" + return + fi + local part sum ro dev + ntfs_extra_space_sz=0 + for part in /dev/disk/by-partuuid/*; do + # Skip empty/ro devices + dev="$( readlink -f "$part" )" + dev="${dev##*/}" + ro="$( cat "/sys/class/block/${dev}/ro" )" + [ "$ro" = 1 ] && continue + # Only count ranges >= 256MB, sum will be in number of 512b blocks + sum="$( ntfsfree --block-size 512 --min-size "$(( 256 * 1024 * 1024 ))" "$part" 2> /dev/null \ + | awk -v sum=0 '{if ($1 == "Range") sum += $4}END{printf "%.0f", sum}' )" + # Only consider volume if sum of these ranges > 1GB (this is BLOCKS, not bytes) + (( "$sum" > 2 * 1024 * 1024 )) || continue + echo "$sum $part" # only thing in loop going to stdout + (( ntfs_extra_space_sz += sum )) + done | sort -nr > "$ntfs_list" +} +ntfs_extra_space_sz=0 + +### +## MAIN +### + +. /etc/openslx + +. slx-tools +# "Preload" functions by executing them NOT in a subshell +dev_find_partitions &> /dev/null +dev_swap_version &> /dev/null + +# This is the main variable driving this script +declare -g id44_crypted= +declare -g writable_device= +if [ -z "$SLX_WRITABLE_DEVICE_IDENTIFIER" ]; then + SLX_WRITABLE_DEVICE_IDENTIFIER=("44" "87f86132-ff94-4987-b250-444444444444") + # TODO make scripts reading this variable compatible with list of IDs + echo "SLX_WRITABLE_DEVICE_IDENTIFIER='${SLX_WRITABLE_DEVICE_IDENTIFIER[0]}'" >> /etc/openslx + echo "SLX_WRITABLE_DEVICE_IDENTIFIERS='${SLX_WRITABLE_DEVICE_IDENTIFIER[*]}'" >> /etc/openslx +fi +# XXX The fuck? This may or may not be an array? Shit will defintely break some day... +if [ -n "$SLX_WRITABLE_DEVICE_IDENTIFIER" ]; then + declare -a writable_devices + writable_devices=( $( dev_find_partitions "${SLX_WRITABLE_DEVICE_IDENTIFIER[@]}" ) ) + if [[ "${#writable_devices[@]}" -eq 0 && "$SLX_NTFSFREE" != "never" ]] || [ "$SLX_NTFSFREE" = "always" ]; then + find_ntfs_partitions + fi + if [ -s "$ntfs_list" ] || [[ "${#writable_devices[@]}" -gt 1 ]]; then + # More than one device, and/or NTFS space, need linear + tbl="/run/openslx/dmsetup-linear-id44" + pos=0 + grow_max_sz=9999999999 + for dev in "${writable_devices[@]}"; do + max="$(( grow_max_sz - pos ))" + (( max <= 0 )) && break + sz="$( blockdev --getsz "$dev" )" + (( sz > 0 )) || continue + (( sz > max )) && sz="$max" + echo "$pos $sz linear $dev 0" + (( pos += sz )) + done > "$tbl" + if [ -s "$ntfs_list" ]; then + sum= + while read -r sum dev _ || [ -n "$sum" ]; do # each dev + word= + while read -r word range_start_b _ range_sz _ || [ -n "$word" ]; do # each slice of dev + [ "$word" = "Range" ] || continue + (( range_sz > 0 )) || continue + slice_sz="$(( grow_max_sz - pos ))" + (( slice_sz <= 0 )) && break + (( slice_sz > range_sz )) && slice_sz="$range_sz" + # Append line + if echo "$pos $slice_sz linear $dev $range_start_b" >> "$tbl"; then + # Update counter + (( pos += slice_sz )) + else + echo "$0: Could not write new table row into $tbl" + fi + done < <( ntfsfree --block-size 512 --min-size "$(( 256 * 1024 * 1024 ))" "$dev" ) + done < "$ntfs_list" + # Don't try to add NTFS space again later + SLX_NTFSFREE="never" + sed -i "s/^SLX_NTFSFREE.*$/# & # disabled in stage3\nSLX_NTFSFREE='never'/" "/etc/openslx" + rm -f -- "$ntfs_list" + fi + # See if we need a linear target at all + if ! [ -s "$tbl" ]; then + echo "$0: Empty tmp/id44 table, fallback to RAM" + elif [ "$( wc -l < "$tbl" )" -eq 1 ] && [[ "${#writable_devices[@]}" -ge 1 ]]; then + # Only one line, have writable device -> use directly + writable_device="${writable_devices[0]}" + else + # set up linera device + if ! dmsetup_create_noudevsync "id44-group" < "$tbl"; then + echo "$0: Error creating group of id44 devices. Fallback to RAM :-(" + else + writable_device="/dev/mapper/id44-group" + fi + fi + else + # Single device + writable_device="${writable_devices[0]}" + fi +fi +if [ -z "$writable_device" ]; then + echo "$0: Could not find writable device with id '$SLX_WRITABLE_DEVICE_IDENTIFIER'." + ramdisk_fallback +elif is_on "$SLX_ID44_CRYPT"; then + # Config option crypts the entire ID44 device(s), before any slices are taken from it. + if encrypt_device "$writable_device" "id44-crypt"; then + echo "$0: ID44 encrypted" + writable_device="/dev/mapper/id44-crypt" + # Remember the whole device is already encrypted, and ignore the crypt flag for the partition table later + id44_crypted=1 + else + echo "$0: Error encrypting ID44 partition" + fi +fi + +# NOTE: from here on out, every value related to size is in 512 bytes sectors! +declare -rg writable_device_sz="$( blockdev --getsz "$writable_device" )" + +# If SLX_WRITABLE_DEVICE_PARTITION_TABLE is not set, just do +# regular thin-snapshot for the CoW layer, else parse it. +if [ -z "$SLX_WRITABLE_DEVICE_PARTITION_TABLE" ]; then + SLX_WRITABLE_DEVICE_PARTITION_TABLE="thin-snapshot root 100% 0" +fi + +# extra swap? +if grep -qFw 'slx.swap' "/proc/cmdline"; then + # Only if our basic writable_device is large enough, or we have ntfs backup + do_swap_sz=0 + if (( writable_device_sz > 80078125 )); then + # more than ~40GB, go ahead + do_swap_sz="$(( ( writable_device_sz - 70312500 ) / 2 ))" + # cap to 6GB + (( do_swap_sz > 11718750 )) && do_swap_sz=11718750 + elif [ "$SLX_NTFSFREE" = "backup" ] \ + && (( ntfs_extra_space_sz > 70312500 )) && (( writable_device_sz > 11718750 )); then + # more than 40GB NTFS backup space, more than 6GB ID44, make 4GB swap + do_swap_sz=7812500 + fi + # Check how many we have and if they're regular, unencrypted ones. + # If it's plenty, don't cut out swap from our backing device + swap_sz=0 + for part in $( dev_find_partitions "82" "0657fd6d-a4ab-43c4-84e5-0933c84b4f4f" ); do + dev_swap_version "$part" &> /dev/null || continue + this_sz="$( blockdev --getsz "$part" )" + (( this_sz > 0 )) && (( swap_sz += this_sz )) + done + echo "Have existing swap of $swap_sz blocks" + # Go ahead with swap? Only if existing swap < 4GB. If so, add line to table. + if (( do_swap_sz > 0 )) && (( swap_sz < 7812500 )); then + echo "Adding $do_swap_sz blocks of additional swap on backing dev" + skb="$(( do_swap_sz / 2 ))" + SLX_WRITABLE_DEVICE_PARTITION_TABLE="$( printf "%s\n%s" "linear slx-swap ${skb}K 0" \ + "$SLX_WRITABLE_DEVICE_PARTITION_TABLE" )" + fi +fi + +parse_config "$SLX_WRITABLE_DEVICE_PARTITION_TABLE" + +# Default to thin-snapshot, if none were configured +if [ "${#snapshot[@]}" = 0 ] && [ "${#thin_snapshot[@]}" = 0 ]; then + parse_config "thin-snapshot root 100% 0" +fi + +# Sanity checks for weird configurations +# XXX These were declared array and now turn into strings... +if [ "${#snapshot[@]}" -gt 1 ]; then + echo "Multiple snapshots specified, using first one: ${snapshot[0]}" +fi +snapshot="${snapshot[0]}" +if [ "${#thin_snapshot[@]}" -gt 1 ]; then + echo "Multiple thin-snapshots specified, using first one: ${thin_snapshot[0]}" +fi +thin_snapshot="${thin_snapshot[0]}" +if [ -n "$snapshot" ] && [ -n "$thin_snapshot" ]; then + echo "$0: Both snapshot and thin-snapshot specified, prefering thin-snapshot." + snapshot= +fi + +### +## LINEAR SLICES +### + +# start allocating spaces to the configured devices +declare -g writable_device_used_sz=0 + +# first, reserve the space for the rootfs cow snapshot (of either type)... +read -r name crypt min max ignore <<< "${thin_snapshot:-${snapshot}}" + +declare -g scratch_device="/dev/mapper/scratch" +declare -gi scratch_device_sz=0 +if (( min <= writable_device_sz )); then + scratch_device_sz="$max" + (( scratch_device_sz > writable_device_sz )) && scratch_device_sz="$writable_device_sz" +else + # minimum snapshot size is bigger than physical device size + echo "$0: Minimum snapshot size is too big for the scratch partition." + echo "$0: You probably need to use a more conservative value." + echo "$0: Using this client maximum scratch space ($writable_device_sz sectors)." + scratch_device_sz="$writable_device_sz" +fi + +# Create a linear target for the scratch device. This might seem superfluous, +# but it works around problems when using NVMe as pool data device directly. +if ! dmsetup_create_noudevsync "${scratch_device##*/}" \ + "0 $scratch_device_sz linear $writable_device $writable_device_used_sz"; then + echo "$0: Failed to create scratch space for the CoW layer." + # this should never fail, but if it does, we would likely not be able to use + # $writable_device for any dmsetup stuff, so just fallback to ramdisk + # until we have a better idea on what to do :) + ramdisk_fallback +fi +save_partition_info "${scratch_device##*/}" "*" "1" "$scratch_device_sz" + +# encrypt the scratch device, if configured +if [ -z "$id44_crypted" ]; then + if [ "$crypt" -ne 0 ] && encrypt_device \ + "$scratch_device" "${scratch_device##*/}-crypt" "$scratch_device_sz"; then + scratch_device="/dev/mapper/${scratch_device##*/}-crypt" + else + echo "$0: Continuing with unencrypted scratch" + fi +fi + +writable_device_used_sz="$scratch_device_sz" + +# setup linear slices of the writable device +for line in "${linear[@]}"; do + [ -z "$line" ] && continue + read -r name crypt min max ignore <<< "$line" + [ -n "$id44_crypted" ] && crypt=0 + free_space="$(( writable_device_sz - writable_device_used_sz ))" + if [ "$min" -gt "$free_space" ]; then + echo "$0: Not enough space left for linear devices: '$line'" + break + fi + # allocate its max if it fits within the free space, otherwise use the space left. + to_allocate="$max" + [ "$to_allocate" -gt "$free_space" ] && to_allocate="$free_space" + + if ! dmsetup_create_noudevsync "$name" "0 $to_allocate linear $writable_device $writable_device_used_sz"; then + echo "$0: Failed to create linear device: $line" + continue + fi + # TODO sane? + save_partition_info "$name" "*" "1" "$to_allocate" + if [ "$crypt" -ne 0 ] && \ + ! encrypt_device "/dev/mapper/$name" "${name}-crypt" "$to_allocate"; then + echo "$0: Failed to encrypt '$name'." + fi + writable_device_used_sz=$(( to_allocate + writable_device_used_sz )) +done + +### +## THIN-PROVISIONING +### +declare -rg pool_dev="/dev/mapper/pool" +declare -gi root_ntfs_extra=0 # Extra blocks to provision to root fs for later expansion +# Now decide what to do for the writable layer + +if [ -n "$thin_snapshot" ] || [ -n "$thin_volume" ]; then + if ! create_pool ; then + echo "Failed to create thin pool. Will ignore:" + echo -e "\tThin snapshot: $(declare -p thin_snapshot)" + echo -e "\tThin volumes: $(declare -p thin_volume)" + echo "Trying snapshot fallback..." + snapshot="$thin_snapshot" + else + # the order in which pool devices are created does not matter + # so start with thin volumes starting with id 2 and end with + # the thin-snapshot with id 1 which needs to call finish_setup. + volume_id=2 + # go over thin-volumes + for line in "${thin_volume[@]}"; do + [ -z "$line" ] && continue + read -r name crypt min max ignore <<< "$line" + [ -n "$id44_crypted" ] && crypt=0 + # thin-volume can be created with max size, + # since they are overprovisioned anyway. + if ! create_volume "$name" "$(( volume_id++ ))" "$max"; then + echo "Failed to create thin volume '$name'." + fi + save_partition_info "$name" "*" "1" "${scratch_device_sz}-${max}" + if [ "$crypt" -ne 0 ] && ! encrypt_device \ + "/dev/mapper/$name" "$name-crypt" "$max"; then + echo "Failed to encrypt thin volume '$name'." + fi + done + + if [ -n "$thin_snapshot" ]; then + # create thin-snapshot, use first one + read -r name crypt min max ignore <<< "$thin_snapshot" + [ -n "$id44_crypted" ] && crypt=0 + # min/max was used for the pool data device, ignore it here! + # Calculate how much of the CoW space we reserve for changes in the base + # system. Usually all the files in the base system should be static, but + # if someone decided to run apt dist-upgrade, this would change a lot of + # existing blocks, which is bad. + # Use MIN( readonly_size / 2, scratch_size / 10 ) + # until we come up with anything better. + # Given an RO image of 10GB, this gives us: + # 40GB scratch -> 46GB, so initially 36GB free space + # 5GB scratch -> 14.5GB, initially 4.5GB free space + declare -r max_reserved_sz="$(( scratch_device_sz / 10 ))" + reserved_sz="$(( read_only_device_sz / 2 ))" + (( reserved_sz > max_reserved_sz )) && reserved_sz="$max_reserved_sz" + thin_snapshot_sz="$(( scratch_device_sz + read_only_device_sz - reserved_sz ))" + # For later on-demand growing, overprovision by free space we found on + # clean NTFS volumes. This requires a user-space helper to listen for + # dm events in stage4, which should in turn add that free space to the pool-data + if (( root_ntfs_extra > 0 )); then + thin_snapshot_sz="$(( thin_snapshot_sz + root_ntfs_extra ))" + fi + if ! create_volume "$name" 1 "$thin_snapshot_sz" "$read_only_device"; then + echo "Failed to create external snapshot for '$read_only_device'." + ramdisk_fallback + fi + finish_setup "$name" "1" "$thin_snapshot_sz" + fi + echo "$0: Thin volumes defined, but no snapshot. Using tmpfs." + ramdisk_fallback + fi +fi + +### +## SNAPSHOT (OLD FUNCTIONALITY) +### +if [ -n "$snapshot" ] && require_exact_scratch_size; then + read -r name crypt min max ignore <<< "$snapshot" + [ -n "$id44_crypted" ] && crypt=0 + if ! create_snapshot "$name $persist"; then + echo "Failed to create regular snapshot for '$read_only_device' on '$scratch_device'." + else + finish_setup "$name" "1" "$scratch_device_sz" + fi +fi + +# ultimate fallback +ramdisk_fallback +exit 1 diff --git a/modules.d/slx-dmsetup/hooks/s3-grow-rootfs.sh b/modules.d/slx-dmsetup/hooks/s3-grow-rootfs.sh new file mode 100755 index 00000000..b93c5658 --- /dev/null +++ b/modules.d/slx-dmsetup/hooks/s3-grow-rootfs.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +# This tries to call growfs helpers (xfs_growfs, resize2fs, ...) to resize +# the root filesystem mounted on $NEWROOT to the maximum size of the backing +# disk partition (done by dmsetup-slx-device). + +. /etc/openslx + +declare -Ag growfs_helpers growfs_targets growfs_opts +# xfs +growfs_helpers[xfs]="xfs_growfs" +growfs_targets[xfs]="$NEWROOT" +growfs_opts[xfs]="-d" +# ext4 +growfs_helpers[ext4]="resize2fs" +growfs_targets[ext4]="$SLX_DNBD3_DEVICE_COW" +growfs_opts[ext4]="" + +resize_rootfs() { + # First let's check what filesystem it is + local fstype="$(blkid "$SLX_DNBD3_DEVICE_COW" | grep -oE 'TYPE=\S+')" + if [ -z "$fstype" ]; then + echo "Failed to detect filesystem on '$SLX_DNBD3_DEVICE_COW' - ignoring." + return 1 + fi + fstype="${fstype#TYPE=}" + fstype="${fstype//\"/}" + if [ ! "${growfs_helpers[${fstype}]+set}" ]; then + echo "'$fstype' not supported - ignoring." + return 1 + fi + if ! hash "${growfs_helpers[${fstype}]}" &> /dev/null; then + echo "'$fstype' is supported, but cannot find helper binary - ignoring." + return 1 + fi + if ! "${growfs_helpers[${fstype}]}" ${growfs_opts[$fstype]} "${growfs_targets[$fstype]}"; then + echo "Failed to run '${growfs_helpers[${fstype}]}' on '${growfs_targets[$fstype]}'." + return 1 + fi + return 0 +} + +[ -b "$SLX_DNBD3_DEVICE_COW" ] && resize_rootfs +# non-critical, so always fake success +true diff --git a/modules.d/slx-dmsetup/module-setup.sh b/modules.d/slx-dmsetup/module-setup.sh index e478a5f7..3bbbd8f3 100755 --- a/modules.d/slx-dmsetup/module-setup.sh +++ b/modules.d/slx-dmsetup/module-setup.sh @@ -6,9 +6,18 @@ depends() { echo "haveged slx-tools" } install() { - inst "$moddir/scripts/dmsetup-slx-device" "/usr/local/bin/dmsetup-slx-device" + inst "$moddir/hooks/dmsetup-slx-device" "/usr/local/bin/dmsetup-slx-device" - inst_hook pre-pivot 00 "$moddir/scripts/grow-rootfs.sh" + # Grows the rootfs to match the underlying blockdev + _name="s3-grow-rootfs" + inst "$moddir/hooks/${_name}.sh" \ + "/usr/local/bin/${_name}.sh" + inst_simple "${moddir}/services/${_name}.service" \ + "${systemdsystemunitdir}/${_name}.service" + mkdir --parents \ + "${initdir}/${systemdsystemunitdir}/initrd.target.requires" + ln_r "${systemdsystemunitdir}/${_name}.service" \ + "${systemdsystemunitdir}/initrd.target.requires/${_name}.service" inst_multiple blockdev xxd \ mkfs.ext4 resize2fs \ diff --git a/modules.d/slx-dmsetup/scripts/dmsetup-slx-device b/modules.d/slx-dmsetup/scripts/dmsetup-slx-device deleted file mode 100755 index 195ab3ad..00000000 --- a/modules.d/slx-dmsetup/scripts/dmsetup-slx-device +++ /dev/null @@ -1,789 +0,0 @@ -#!/usr/bin/env bash -# -# Script to back given read-only device using the block device -# specified by SLX_WRITABLE_DEVICE_IDENTIFIER in the SLX config. -# If SLX_WRITABLE_DEVICE_PARTITION_TABLE is sepcified, it will -# further create device mapper devices accordingly. -# -# Example partition config: -# -# thin-snapshot root 10G 1 -# thin-volume tmp 20G 0 -# linear data0 5-10G 1 -# linear data1 1-50% 1 -# -# NOTE: Encrypting thin-snapshot will actually encrypt the -# entire pool data device used for the pool. -# TODO: Support external keys -# TODO: Put table in file in config.tgz - -type -p emergency_shell || . /lib/dracut-lib.sh - -# for debugging purposes -exec {BASH_XTRACEFD}> /run/openslx/dmsetup.log -set -x - -# read-only device to prepare for CoW -[ -z "$1" ] && emergency_shell "Read-only device was not given!" -declare -g read_only_device="$1" -declare -g read_only_device_sz="$( blockdev --getsz "$1" )" -# Use _sz suffix for sizes expressed in number of 512b sectors, -# _size for random other crap - -declare -rg ntfs_list="/run/openslx/.thin-ntfs-candidates" - -# handle_unit -# Supply percentage, or size in [kmgt]bytes, -# returns appropriate value in number of 512b sectors -handle_unit() { - # default to bytes - local -i potency=0 - local -i val="$1" - case "$2" in - [%]) # These are relative to the writable CoW device - # Allow > 100% for over-provisioning - val="$(( remaining_device_sz * val / 100 ))" - ;; - [Kk]) potency=1 ;;& - [Mm]) potency=2 ;;& - [Gg]) potency=3 ;;& - [Tt]) potency=4 ;;& - *) - # => 1024 ** potency for G, M, K, etc results in bytes - # => bytes / 512 = sectors - val=$(( val * ( 1024 ** potency) / 512 )) - ;; - esac - echo "$val" -} - -parse_config() { - local remaining_device_sz="$writable_device_sz" - parse_config_int "$1" 0 - parse_config_int "$1" 1 -} - -# global array variables storing the configuration of the partitions -declare -ag linear snapshot thin_snapshot thin_volume -parse_config_int() { - [ -z "$1" ] && return 1 - local -i rel_only="$2" - while IFS= read -r line || [ -n "$line" ]; do - [ -z "$line" ] && continue - read -r type name range crypt ignore <<< "$line" - type=${type//-/_} # to use the type as variable in eval - if ! [[ "$type" =~ \ - ^(linear|snapshot|thin_snapshot|thin_volume)$ ]]; then - echo "$0: Ignoring invalid type: $line" - continue - fi - if [[ -z "$name" ]]; then - echo "$0: Ignoring nameless entry: $line" - continue - fi - unset min_unit max_unit min max - # ranges can be like: 40G, 40-80G, 10G-20% - if ! [[ "$range" =~ ^([0-9]+)([GgMmKkBb%]?)(-([0-9]+)([GgMmKkBb%]?))?$ ]]; then - echo "$0: Ignoring invalid range: $line" - continue - fi - local min="${BASH_REMATCH[1]}" - local max="${BASH_REMATCH[4]:-${BASH_REMATCH[1]}}" - local min_unit="${BASH_REMATCH[2]:-${BASH_REMATCH[5]}}" - local max_unit="${BASH_REMATCH[5]:-${BASH_REMATCH[2]}}" - # first pass we handle absolute values unly, second pass relative ones - if [[ "$min_unit" = "%" || "$max_unit" = "%" ]]; then - [ "$rel_only" != 1 ] && continue - else - [ "$rel_only" = 1 ] && continue - fi - if [ -z "$min_unit" ]; then - echo "$0: WARNING: No unit given in range, assuming BYTES: $line" - fi - min="$( handle_unit "$min" "$min_unit" )" - max="$( handle_unit "$max" "$max_unit" )" - if (( min > max )); then - # So, we might end up with something like 30G-100%, but the writable device - # is only 20GB. In that case we most likely want to contine, and not consider - # this an error. So let's try to come up with some logic on what is an error - # and what isn't. Probably anything involving a mix of percentage and - # non-percentage should not be an error. - if [[ "$min_unit" = "%" && "$max_unit" != "%" ]] \ - || [[ "$min_unit" != "%" && "$max_unit" = "%" ]]; then - # Let's hope for the best - max="$min" - else - echo "$0: Ignoring invalid range (min > max): $line" - continue - fi - fi - if ! [[ "$crypt" =~ ^[01]$ ]]; then - echo "$0: Disabling encryption due to invalid crypt argument: $line" - crypt=0 - fi - # finally save it to the global array for this type - case "$type" in - linear) linear+=("${name} ${crypt} ${min} ${max}") ;; - snapshot) snapshot+=("${name} ${crypt} ${min} ${max}") ;; - thin_snapshot) thin_snapshot+=("${name} ${crypt} ${min} ${max}") ;; - thin_volume) thin_volume+=("${name} ${crypt} ${min} ${max}") ;; - *) echo "$0: SOMETHING NOT GOOT CHECK SOURCE CODE" ;; - esac - # Decrease for upcoming calculations if we used fixed values here - if [ "$rel_only" != 1 ]; then - (( remaining_device_sz -= ( min + max ) / 2 )) - fi - done <<< "$1" -} - -# Helper to call 'dmsetup setup' without syncing with udev -# and then actively create the devices with the mknodes command. -# Either pass the table contents as $2, or pipe them into the function -# dmsetup_create_noudevsync [table] -dmsetup_create_noudevsync() { - ( - set -eo pipefail - if [ -n "$2" ]; then - printf "%s\n" "$2" | dmsetup create "$1" --noudevsync - else - dmsetup create "$1" --noudevsync - fi - dmsetup mknodes --noudevsync "$1" - ) - local ret=$? - [ -b "/dev/mapper/$1" ] || ret=99 - [ $ret -ne 0 ] && dmsetup remove --noudevsync "$1" - return $ret -} - -# encrypt_device [] -encrypt_device() { - # TODO: Send key back to us, demand ransom - modprobe dm-crypt || echo "$0: dm-crypt loading failed, maybe builtin?" - [ -b "$1" ] || return 1 - [ -n "$2" ] || return 1 - [ -z "$3" ] && local size="$( blockdev --getsz "$1" )" - local key - key="$( < /dev/urandom xxd -c32 -p -l32 )" - [ -z "$key" ] && key="$( < /dev/urandom tr -c -d 'a-f0-9' | dd count=1 bs=32 )" - [ -z "$key" ] && key="$( < /dev/urandom head -c32 | xxd -c32 -p )" - [ -z "$key" ] && key="$( < /dev/urandom xxd -c32 -p | head -n 1 )" - [ -z "$key" ] && echo "$0: ERROR: Could not generate encryption key" - if ! dmsetup_create_noudevsync "$2" \ - "0 ${3:-${size}} crypt aes-xts-plain64 $key 0 $1 0 1 allow_discards"; then - echo "$0: Failed to encrypt $1." - return 1 - fi - return 0 -} -# create_snapshot " " -create_snapshot() { - modprobe dm-snapshot || echo "$0: dm-snapshot loading failed, maybe builtin?" - read -r name persist ignore <<< "$1" - if ! dmsetup_create_noudevsync "$name" \ - "0 $read_only_device_sz snapshot $read_only_device $writable_device ${persist:-N} 8"; then - echo "$0: Failed to create snapshot on '$writable_device' for '$read_only_device'." - return 1 - fi - return 0 -} - -# This function is called if no ID44 partition could be found or anoother kind -# of critical error occurs during the CoW layer setup. It will combine the -# the read-only device with a DM zero device to increase its virtual size -# by half the RAM size. A sparse file of that size will then be created and -# placed on a dedicated tmpfs. -# THIS FUNCTION MUST NEVER RETURN -ramdisk_fallback() { - echo "$0: Falling back to regular dm-snapshot on a RAMdisk." - - # RAM size in kb, note that this is equal to half - # of the entire RAM when interpreted as 512-bytes sectors. - local ram_cow_sz="$(awk '/^MemTotal:/ { printf("%d\n", $2 ); exit }' /proc/meminfo)" - - # try to prepare the zero extension device - local extended_device="/dev/mapper/${read_only_device##*/}-extended" - ( - set -e - lsmod | grep -q dm-zero || modprobe dm-zero - dmsetup_create_noudevsync "${extended_device##*/}" \ - "0 $read_only_device_sz linear $read_only_device 0 - $read_only_device_sz $ram_cow_sz zero" - ) - local ret="$?" - if [ "$ret" -eq 0 ]; then - read_only_device="$extended_device" - read_only_device_sz="$(( read_only_device_sz + ram_cow_sz ))" - else - echo "$0: Failed to setup the fake larger '$read_only_device'." - echo "$0: Continuing with its original size." - fi - - # prepare dedicated tmpfs mount point - local cow_tmpfs="/run/openslx/cow" - if ! mkdir -p "$cow_tmpfs"; then - cow_tmpfs="${cow_tmpfs}.$$.$RANDOM" - mkdir -p "$cow_tmpfs" - fi - if ! mount -t tmpfs cow-tmpfs -o size="$(( read_only_device_sz / 2 + 100 ))k" "$cow_tmpfs"; then - echo "$0: Failed to mount tmpfs in '$cow_tmpfs' of size '$(( read_only_device_sz / 2 + 100 ))KiB'." - fi - - # create sparse file there - local file="$(mktemp -u -p "$cow_tmpfs" dnbd_cow.XXX)" - if ! dd if=/dev/null of="$file" seek="$(( read_only_device_sz ))" bs=512 2> /dev/null; then - emergency_shell "Failed to allocate CoW file $file." - fi - declare -rg writable_device="$(losetup --show --find "$file")" - local cow_device_candidate="root" - while [ -b "/dev/mapper/$cow_device_candidate" ]; do - cow_device_candidate="root.$RANDOM" - done - if [ -z "$writable_device" ] || ! create_snapshot "$cow_device_candidate N"; then - emergency_shell "CRITICAL: failed to setup RAMdisk fallback." - exit 1 - fi - finish_setup "$cow_device_candidate" "0" "$read_only_device_sz" -} - -# finish_setup [] -# is the device name only, /dev/mapper will be prepended automatically. -# denotes if the created device lies in a RAMdisk (0) or is backed by a disk (1). -# is given in sectors. -# THIS FUNCTION MUST NEVER RETURN -finish_setup() { - if [ -z "$1" ] || ! [ -b "/dev/mapper/$1" ]; then - emergency_shell "'/dev/mapper/$1' not a block device. Failed to setup CoW layer." - exit 1 - fi - if ! [[ "$2" =~ ^[0-9]$ ]]; then - emergency_shell "'$2' not a valid type, 0 or 1 expected." - fi - # optional? - { - echo "# Generated by '$0'." - echo "SLX_DNBD3_DEVICE_COW=/dev/mapper/$1" - } >> /etc/openslx - save_partition_info "$1" "/" "$2" "$3" - exit 0 -} - -# path to save the achieved setup to -declare -rg partitions_config="/run/openslx/dmsetup.state" -cat <<-EOF > "$partitions_config" -# Generated by '$0'. -# Format: -# Options can be: -# * type -> CoW layer type: 0 is RAMdisk, 1 is disk, 2 is network -# * size -> in 512 byte sectors -EOF - -# save_partition_info [] -save_partition_info() { - [ -b "/dev/mapper/$1" ] || return 1 - [ -n "$2" ] || return 1 - [[ "$3" =~ ^[0-9]$ ]] || return 1 - local opts="type=$3" - # plain size given - [[ "$4" =~ ^[0-9]+$ ]] && opts="$opts,physical_size=$4" - # - - [[ "$4" =~ ^[0-9]+-[0-9]+$ ]] && opts="$opts,shared_physical_size=${4%-*},virtual_size=${4#*-}" - echo "/dev/mapper/$1 $2 ${opts}" >> "$partitions_config" -} - -# This will create another dm-linear on top of $scratch_device in case its -# size differs from $scratch_device_sz. This is useful for setups where you -# cannot explicitly configure how much space to use from the underlying device, -# and the partition table says not to use the entire $writable_device for cow -require_exact_scratch_size() { - local current_sz="$( blockdev --getsz "$scratch_device" )" - (( current_sz == scratch_device_sz )) && return 0 # Everything fine - if (( current_sz < scratch_device_sz )); then - echo "$0: WARNING: scratch_device_sz is larger than actual device." - echo "$0: This should never happen." - scratch_device_sz="$current_sz" - return 0 - fi - # We could check if $scratch_device already is a dm target, and just adjust its - # size, but I think that scenario isn't possible, currently. - if ! dmsetup_create_noudevsync "scratch" "0 $scratch_device_sz linear $scratch_device 0"; then - echo "$0: Failed to create scratch space for the CoW layer." - return 1 - fi - scratch_device="/dev/mapper/scratch" - save_partition_info "scratch" "*" "1" "$scratch_device_sz" - return 0 -} - -create_pool() { - declare -r data_block_sz=256 # Desired Block size (number of 512byte sectors) - declare -r wanted_low_mb=100 # Free space below this will trigger a dm event - # create external snapshot for read-only device - # create remaining thin volumes - modprobe dm-thin-pool || echo "$0: dm-thin-pool load failed, maybe builtin?" - # create temporary metadata device - # calculate number of sectors needed and check boundaries: - # XXX Formula from thin-pool.txt calculates size in *bytes*, we want 512b blocks - metadata_dev_sz="$(( 48 * scratch_device_sz / data_block_sz / 512 ))" - # If we want NTFS as a backup plan to extend the pool, check if the current size - # is less than 100GB, and only then consider this feature. - # Maybe make that thresold configurable one day, but the the desktop client - # use case this is sensible for now. - if [ "$SLX_NTFSFREE" = "backup" ] && (( scratch_device_sz < 209715200 )) \ - && [ -z "$metadata_persistent" ]; then - find_ntfs_partitions - if [ -s "$ntfs_list" ]; then - # Look what size we end up if we want at least 50GB - local sum="$( awk -v sum=0 \ - '{sum+=$1; if (sum >= 104857600) exit}END{printf "%.0f", sum}' \ - "$ntfs_list" )" - if (( sum > 0 )); then - (( sum > 209715200 )) && sum=209715200 # Max 100GB - # Account for this potential growth in the metadata device size for future expansion - metadata_dev_sz="$(( metadata_dev_sz + 48 * sum / data_block_sz / 512 ))" - echo "$sum" > "/run/openslx/.thin-ntfs-growsize" - root_ntfs_extra="$sum" - fi - fi - fi - # Min 2MB -> 4096 sectors, max 16GB -> 33554432 sectors - [ "$metadata_dev_sz" -lt 4096 ] && metadata_dev_sz="4096" - # TODO handle the exotic case of a too large metadata device to fit within RAM. - [ "$metadata_dev_sz" -gt 33554432 ] && metadata_dev_sz="33554432" - local scratch_device_offset=0 - local metadata_dev= - local metadata_persistent= - if [ -n "$metadata_persistent" ]; then - # create persistent slice of the writable device for the pool metadata - if ! dmsetup_create_noudevsync "pool-metadata" \ - "0 $metadata_dev_sz linear $scratch_device $scratch_device_offset"; then - echo "$0: Failed to create linear device for pool metadata device." - else - # Adjust size for pool-data down accordingly - scratch_device_offset="$metadata_dev_sz" - scratch_device_sz=$(( scratch_device_sz - metadata_dev_sz )) - declare -r metadata_dev="/dev/mapper/pool-metadata" - # TODO configurable wipe: dd if=/dev/zero of="$metadata_dev" count=1 bs=4096 - # TODO: If we fail later on in this function, we would actually have to destroy - # this target again, and re-adjust the offset and size back, so that the - # snapshot fallback would work properly. Or maybe just don't support fallback. - fi - fi - if [ -z "$metadata_dev" ]; then - # create RAMdisk in /run for metadata device - metadata_dev="$(mktemp -p /run/openslx .pool-metadata.XXX)" - # Create sparse file of required size - dd if=/dev/null of="$metadata_dev" bs=512 seek="$metadata_dev_sz" 2> /dev/null - declare -r metadata_dev="$( losetup --show --find "$metadata_dev" )" - fi - if [ -z "$metadata_dev" ]; then - echo "$0: Could not set up persistent or tmpfs-loop metadata device. Aborting." - return 1 - fi - - local pool_data_dev - if (( root_ntfs_extra == 0 )) && (( scratch_device_offset == 0 )); then - # No offset, no potential expansion, don't create another linear target - pool_data_dev="$scratch_device" - else - pool_data_dev="/dev/mapper/pool-data" - # Create linear device of the writable device, in case we have an offset from - # the on-disk meta data. Also this way we can easily extend it later. - if ! dmsetup_create_noudevsync "${pool_data_dev##*/}" \ - "0 $scratch_device_sz linear $scratch_device $scratch_device_offset"; then - echo "$0: Failed to create pool data device on '$scratch_device'." - return 1 - fi - fi - local low_water_mark - # Convert MB to blocks - low_water_mark=$(( wanted_low_mb * 2048 / data_block_sz )) - if ! dmsetup_create_noudevsync "${pool_dev##*/}" \ - "0 $scratch_device_sz thin-pool $metadata_dev $pool_data_dev $data_block_sz $low_water_mark 1 skip_block_zeroing"; then - echo "$0: Failed to create thin-pool device (meta: $metadata_dev, data: $pool_data_dev)" - return 1 - fi - return 0 -} - -# create_volume [backing_dev] -create_volume() { - if [ -z "$pool_dev" ] || ! [ -b "$pool_dev" ]; then - echo "$0: Global pool device not set or present." - return 1 - fi - if [ $# -lt 3 ] || [ -z "$1" ]; then - echo "$0: create_volume: not enough arguments." - return 1 - fi - local name="$1" - local id="$2" - local size="$3" - local backing_dev="$4" # Optional, internal if empty - - if ! dmsetup message "$pool_dev" 0 "create_thin $id"; then - echo "$0: Failed to create thin volume with id '$id' in pool '$pool_dev'." - echo "$0: It might already exists, trying anyway..." - fi - if ! dmsetup_create_noudevsync "$name" "0 $size thin $pool_dev $id $backing_dev"; then - echo "$0: Failed to create external snapshot named '$name':" - echo " Size: $size" - echo " Backing device: $backing_dev" - echo " Thin volume id: $id" - return 1 - fi - return 0 -} - -# Find NTFS partitions with decently sized ranges of -# free space. We can use these as our writable layer -# for our thin-pool, if configured. -# If suitable, this will create the file $ntfs_list with -# one line per suitable partition, format -# total_size_blocks devpath -# Results are sorted by size, descending order -find_ntfs_partitions() { - [ -z "$SLX_NTFSFREE" ] && return - [ "$SLX_NTFSFREE" = "never" ] && return - [ -e "$ntfs_list" ] && return - if ! command -v ntfsfree &> /dev/null; then - echo "$0: ntfsfree not found, cannot use NTFS partitions as RW layer" - return - fi - local part sum - ntfs_extra_space_sz=0 - for part in /dev/disk/by-partuuid/*; do - # Only count ranges >= 256MB, sum will be in number of 512b blocks - sum="$( ntfsfree --block-size 512 --min-size "$(( 256 * 1024 * 1024 ))" "$part" \ - | awk -v sum=0 '{if ($1 == "Range") sum += $4}END{printf "%.0f", sum}' )" - # Only consider volume if sum of these ranges > 1GB (this is BLOCKS, not bytes) - (( "$sum" > 2 * 1024 * 1024 )) || continue - echo "$sum $part" # only thing in loop going to stdout - (( ntfs_extra_space_sz += sum )) - done | sort -nr > "$ntfs_list" -} -ntfs_extra_space_sz=0 - -### -## MAIN -### - -. /etc/openslx - -. slx-tools -# "Preload" functions by executing them NOT in a subshell -dev_find_partitions &> /dev/null -dev_swap_version &> /dev/null - -# This is the main variable driving this script -declare -g id44_crypted= -declare -g writable_device= -if [ -z "$SLX_WRITABLE_DEVICE_IDENTIFIER" ]; then - SLX_WRITABLE_DEVICE_IDENTIFIER=("44" "87f86132-ff94-4987-b250-444444444444") - # TODO make scripts reading this variable compatible with list of IDs - echo "SLX_WRITABLE_DEVICE_IDENTIFIER='${SLX_WRITABLE_DEVICE_IDENTIFIER[0]}'" >> /etc/openslx - echo "SLX_WRITABLE_DEVICE_IDENTIFIERS='${SLX_WRITABLE_DEVICE_IDENTIFIER[*]}'" >> /etc/openslx -fi -# XXX The fuck? This may or may not be an array? Shit will defintely break some day... -if [ -n "$SLX_WRITABLE_DEVICE_IDENTIFIER" ]; then - declare -a writable_devices - writable_devices=( $( dev_find_partitions "${SLX_WRITABLE_DEVICE_IDENTIFIER[@]}" ) ) - if [[ "${#writable_devices[@]}" -eq 0 && "$SLX_NTFSFREE" != "never" ]] || [ "$SLX_NTFSFREE" = "always" ]; then - find_ntfs_partitions - fi - if [ -s "$ntfs_list" ] || [[ "${#writable_devices[@]}" -gt 1 ]]; then - # More than one device, and/or NTFS space, need linear - tbl="/run/openslx/dmsetup-linear-id44" - pos=0 - grow_max_sz=9999999999 - for dev in "${writable_devices[@]}"; do - max="$(( grow_max_sz - pos ))" - (( max <= 0 )) && break - sz="$( blockdev --getsz "$dev" )" - (( sz > 0 )) || continue - (( sz > max )) && sz="$max" - echo "$pos $sz linear $dev 0" - (( pos += sz )) - done > "$tbl" - if [ -s "$ntfs_list" ]; then - sum= - while read -r sum dev _ || [ -n "$sum" ]; do # each dev - word= - while read -r word range_start_b _ range_sz _ || [ -n "$word" ]; do # each slice of dev - [ "$word" = "Range" ] || continue - (( range_sz > 0 )) || continue - slice_sz="$(( grow_max_sz - pos ))" - (( slice_sz <= 0 )) && break - (( slice_sz > range_sz )) && slice_sz="$range_sz" - # Append line - if echo "$pos $slice_sz linear $dev $range_start_b" >> "$tbl"; then - # Update counter - (( pos += slice_sz )) - else - echo "$0: Could not write new table row into $tbl" - fi - done < <( ntfsfree --block-size 512 --min-size "$(( 256 * 1024 * 1024 ))" "$dev" ) - done < "$ntfs_list" - # Don't try to add NTFS space again later - SLX_NTFSFREE="never" - sed -i "s/^SLX_NTFSFREE.*$/# & # disabled in stage3\nSLX_NTFSFREE='never'/" "/etc/openslx" - rm -f -- "$ntfs_list" - fi - # See if we need a linear target at all - if ! [ -s "$tbl" ]; then - echo "$0: Empty tmp/id44 table, fallback to RAM" - elif [ "$( wc -l < "$tbl" )" -eq 1 ] && [[ "${#writable_devices[@]}" -ge 1 ]]; then - # Only one line, have writable device -> use directly - writable_device="${writable_devices[0]}" - else - # set up linera device - if ! dmsetup_create_noudevsync "id44-group" < "$tbl"; then - echo "$0: Error creating group of id44 devices. Fallback to RAM :-(" - else - writable_device="/dev/mapper/id44-group" - fi - fi - else - # Single device - writable_device="${writable_devices[0]}" - fi -fi -if [ -z "$writable_device" ]; then - echo "$0: Could not find writable device with id '$SLX_WRITABLE_DEVICE_IDENTIFIER'." - ramdisk_fallback -elif is_on "$SLX_ID44_CRYPT"; then - # Config option crypts the entire ID44 device(s), before any slices are taken from it. - if encrypt_device "$writable_device" "id44-crypt"; then - echo "$0: ID44 encrypted" - writable_device="/dev/mapper/id44-crypt" - # Remember the whole device is already encrypted, and ignore the crypt flag for the partition table later - id44_crypted=1 - else - echo "$0: Error encrypting ID44 partition" - fi -fi - -# NOTE: from here on out, every value related to size is in 512 bytes sectors! -declare -rg writable_device_sz="$( blockdev --getsz "$writable_device" )" - -# If SLX_WRITABLE_DEVICE_PARTITION_TABLE is not set, just do -# regular thin-snapshot for the CoW layer, else parse it. -if [ -z "$SLX_WRITABLE_DEVICE_PARTITION_TABLE" ]; then - SLX_WRITABLE_DEVICE_PARTITION_TABLE="thin-snapshot root 100% 0" -fi - -# extra swap? -if grep -qFw 'slx.swap' "/proc/cmdline"; then - # Only if our basic writable_device is large enough, or we have ntfs backup - do_swap_sz=0 - if (( writable_device_sz > 80078125 )); then - # more than ~40GB, go ahead - do_swap_sz="$(( ( writable_device_sz - 70312500 ) / 2 ))" - # cap to 6GB - (( do_swap_sz > 11718750 )) && do_swap_sz=11718750 - elif [ "$SLX_NTFSFREE" = "backup" ] \ - && (( ntfs_extra_space_sz > 70312500 )) && (( writable_device_sz > 11718750 )); then - # more than 40GB NTFS backup space, more than 6GB ID44, make 4GB swap - do_swap_sz=7812500 - fi - # Check how many we have and if they're regular, unencrypted ones. - # If it's plenty, don't cut out swap from our backing device - swap_sz=0 - for part in $( dev_find_partitions "82" "0657fd6d-a4ab-43c4-84e5-0933c84b4f4f" ); do - dev_swap_version "$part" &> /dev/null || continue - this_sz="$( blockdev --getsz "$part" )" - (( this_sz > 0 )) && (( swap_sz += this_sz )) - done - echo "Have existing swap of $swap_sz blocks" - # Go ahead with swap? Only if existing swap < 4GB. If so, add line to table. - if (( do_swap_sz > 0 )) && (( swap_sz < 7812500 )); then - echo "Adding $do_swap_sz blocks of additional swap on backing dev" - skb="$(( do_swap_sz / 2 ))" - SLX_WRITABLE_DEVICE_PARTITION_TABLE="$( printf "%s\n%s" "linear slx-swap ${skb}K 0" \ - "$SLX_WRITABLE_DEVICE_PARTITION_TABLE" )" - fi -fi - -parse_config "$SLX_WRITABLE_DEVICE_PARTITION_TABLE" - -# Default to thin-snapshot, if none were configured -if [ "${#snapshot[@]}" = 0 ] && [ "${#thin_snapshot[@]}" = 0 ]; then - parse_config "thin-snapshot root 100% 0" -fi - -# Sanity checks for weird configurations -# XXX These were declared array and now turn into strings... -if [ "${#snapshot[@]}" -gt 1 ]; then - echo "Multiple snapshots specified, using first one: ${snapshot[0]}" -fi -snapshot="${snapshot[0]}" -if [ "${#thin_snapshot[@]}" -gt 1 ]; then - echo "Multiple thin-snapshots specified, using first one: ${thin_snapshot[0]}" -fi -thin_snapshot="${thin_snapshot[0]}" -if [ -n "$snapshot" ] && [ -n "$thin_snapshot" ]; then - echo "$0: Both snapshot and thin-snapshot specified, prefering thin-snapshot." - snapshot= -fi - -### -## LINEAR SLICES -### - -# start allocating spaces to the configured devices -declare -g writable_device_used_sz=0 - -# first, reserve the space for the rootfs cow snapshot (of either type)... -read -r name crypt min max ignore <<< "${thin_snapshot:-${snapshot}}" - -declare -g scratch_device="/dev/mapper/scratch" -declare -gi scratch_device_sz=0 -if (( min <= writable_device_sz )); then - scratch_device_sz="$max" - (( scratch_device_sz > writable_device_sz )) && scratch_device_sz="$writable_device_sz" -else - # minimum snapshot size is bigger than physical device size - echo "$0: Minimum snapshot size is too big for the scratch partition." - echo "$0: You probably need to use a more conservative value." - echo "$0: Using this client maximum scratch space ($writable_device_sz sectors)." - scratch_device_sz="$writable_device_sz" -fi - -# Create a linear target for the scratch device. This might seem superfluous, -# but it works around problems when using NVMe as pool data device directly. -if ! dmsetup_create_noudevsync "${scratch_device##*/}" \ - "0 $scratch_device_sz linear $writable_device $writable_device_used_sz"; then - echo "$0: Failed to create scratch space for the CoW layer." - # this should never fail, but if it does, we would likely not be able to use - # $writable_device for any dmsetup stuff, so just fallback to ramdisk - # until we have a better idea on what to do :) - ramdisk_fallback -fi -save_partition_info "${scratch_device##*/}" "*" "1" "$scratch_device_sz" - -# encrypt the scratch device, if configured -if [ -z "$id44_crypted" ]; then - if [ "$crypt" -ne 0 ] && encrypt_device \ - "$scratch_device" "${scratch_device##*/}-crypt" "$scratch_device_sz"; then - scratch_device="/dev/mapper/${scratch_device##*/}-crypt" - else - echo "$0: Continuing with unencrypted scratch" - fi -fi - -writable_device_used_sz="$scratch_device_sz" - -# setup linear slices of the writable device -for line in "${linear[@]}"; do - [ -z "$line" ] && continue - read -r name crypt min max ignore <<< "$line" - [ -n "$id44_crypted" ] && crypt=0 - free_space="$(( writable_device_sz - writable_device_used_sz ))" - if [ "$min" -gt "$free_space" ]; then - echo "$0: Not enough space left for linear devices: '$line'" - break - fi - # allocate its max if it fits within the free space, otherwise use the space left. - to_allocate="$max" - [ "$to_allocate" -gt "$free_space" ] && to_allocate="$free_space" - - if ! dmsetup_create_noudevsync "$name" "0 $to_allocate linear $writable_device $writable_device_used_sz"; then - echo "$0: Failed to create linear device: $line" - continue - fi - # TODO sane? - save_partition_info "$name" "*" "1" "$to_allocate" - if [ "$crypt" -ne 0 ] && \ - ! encrypt_device "/dev/mapper/$name" "${name}-crypt" "$to_allocate"; then - echo "$0: Failed to encrypt '$name'." - fi - writable_device_used_sz=$(( to_allocate + writable_device_used_sz )) -done - -### -## THIN-PROVISIONING -### -declare -rg pool_dev="/dev/mapper/pool" -declare -gi root_ntfs_extra=0 # Extra blocks to provision to root fs for later expansion -# Now decide what to do for the writable layer - -if [ -n "$thin_snapshot" ] || [ -n "$thin_volume" ]; then - if ! create_pool ; then - echo "Failed to create thin pool. Will ignore:" - echo -e "\tThin snapshot: $(declare -p thin_snapshot)" - echo -e "\tThin volumes: $(declare -p thin_volume)" - echo "Trying snapshot fallback..." - snapshot="$thin_snapshot" - else - # the order in which pool devices are created does not matter - # so start with thin volumes starting with id 2 and end with - # the thin-snapshot with id 1 which needs to call finish_setup. - volume_id=2 - # go over thin-volumes - for line in "${thin_volume[@]}"; do - [ -z "$line" ] && continue - read -r name crypt min max ignore <<< "$line" - [ -n "$id44_crypted" ] && crypt=0 - # thin-volume can be created with max size, - # since they are overprovisioned anyway. - if ! create_volume "$name" "$(( volume_id++ ))" "$max"; then - echo "Failed to create thin volume '$name'." - fi - save_partition_info "$name" "*" "1" "${scratch_device_sz}-${max}" - if [ "$crypt" -ne 0 ] && ! encrypt_device \ - "/dev/mapper/$name" "$name-crypt" "$max"; then - echo "Failed to encrypt thin volume '$name'." - fi - done - - if [ -n "$thin_snapshot" ]; then - # create thin-snapshot, use first one - read -r name crypt min max ignore <<< "$thin_snapshot" - [ -n "$id44_crypted" ] && crypt=0 - # min/max was used for the pool data device, ignore it here! - # Calculate how much of the CoW space we reserve for changes in the base - # system. Usually all the files in the base system should be static, but - # if someone decided to run apt dist-upgrade, this would change a lot of - # existing blocks, which is bad. - # Use MIN( readonly_size / 2, scratch_size / 10 ) - # until we come up with anything better. - # Given an RO image of 10GB, this gives us: - # 40GB scratch -> 46GB, so initially 36GB free space - # 5GB scratch -> 14.5GB, initially 4.5GB free space - declare -r max_reserved_sz="$(( scratch_device_sz / 10 ))" - reserved_sz="$(( read_only_device_sz / 2 ))" - (( reserved_sz > max_reserved_sz )) && reserved_sz="$max_reserved_sz" - thin_snapshot_sz="$(( scratch_device_sz + read_only_device_sz - reserved_sz ))" - # For later on-demand growing, overprovision by free space we found on - # clean NTFS volumes. This requires a user-space helper to listen for - # dm events in stage4, which should in turn add that free space to the pool-data - if (( root_ntfs_extra > 0 )); then - thin_snapshot_sz="$(( thin_snapshot_sz + root_ntfs_extra ))" - fi - if ! create_volume "$name" 1 "$thin_snapshot_sz" "$read_only_device"; then - echo "Failed to create external snapshot for '$read_only_device'." - ramdisk_fallback - fi - finish_setup "$name" "1" "$thin_snapshot_sz" - fi - echo "$0: Thin volumes defined, but no snapshot. Using tmpfs." - ramdisk_fallback - fi -fi - -### -## SNAPSHOT (OLD FUNCTIONALITY) -### -if [ -n "$snapshot" ] && require_exact_scratch_size; then - read -r name crypt min max ignore <<< "$snapshot" - [ -n "$id44_crypted" ] && crypt=0 - if ! create_snapshot "$name $persist"; then - echo "Failed to create regular snapshot for '$read_only_device' on '$scratch_device'." - else - finish_setup "$name" "1" "$scratch_device_sz" - fi -fi - -# ultimate fallback -ramdisk_fallback -exit 1 diff --git a/modules.d/slx-dmsetup/scripts/grow-rootfs.sh b/modules.d/slx-dmsetup/scripts/grow-rootfs.sh deleted file mode 100644 index e2603835..00000000 --- a/modules.d/slx-dmsetup/scripts/grow-rootfs.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -# This tried to call growfs helpers (xfs_growfs, resize2fs, ...) to resize -# the root filesystem mounted on $NEWROOT to the maximum size of the backing -# disk partition (done by dmsetup-slx-device). - -. /etc/openslx - -declare -Ag growfs_helpers growfs_targets growfs_opts -# xfs -growfs_helpers[xfs]="xfs_growfs" -growfs_targets[xfs]="$NEWROOT" -growfs_opts[xfs]="-d" -# ext4 -growfs_helpers[ext4]="resize2fs" -growfs_targets[ext4]="$SLX_DNBD3_DEVICE_COW" -growfs_opts[ext4]="" - -resize_rootfs() { - # First let's check what filesystem it is - local fstype="$(blkid "$SLX_DNBD3_DEVICE_COW" | grep -oE 'TYPE=\S+')" - if [ -z "$fstype" ]; then - echo "Failed to detect filesystem on '$SLX_DNBD3_DEVICE_COW' - ignoring." - return 1 - fi - fstype="${fstype#TYPE=}" - fstype="${fstype//\"/}" - if [ ! "${growfs_helpers[${fstype}]+set}" ]; then - echo "'$fstype' not supported - ignoring." - return 1 - fi - if ! hash "${growfs_helpers[${fstype}]}" &> /dev/null; then - echo "'$fstype' is supported, but cannot find helper binary - ignoring." - return 1 - fi - if ! "${growfs_helpers[${fstype}]}" ${growfs_opts[$fstype]} "${growfs_targets[$fstype]}"; then - echo "Failed to run '${growfs_helpers[${fstype}]}' on '${growfs_targets[$fstype]}'." - return 1 - fi - return 0 -} &> /run/openslx/rootfs-grow.log - -[ -b "$SLX_DNBD3_DEVICE_COW" ] && resize_rootfs -# non-critical, so always fake success -true diff --git a/modules.d/slx-dmsetup/services/s3-grow-rootfs.service b/modules.d/slx-dmsetup/services/s3-grow-rootfs.service new file mode 100644 index 00000000..a849f4a3 --- /dev/null +++ b/modules.d/slx-dmsetup/services/s3-grow-rootfs.service @@ -0,0 +1,12 @@ +[Unit] +Description=Grow size of rootfs to underlying block device +# For ext4, -root-device would be sufficient, but XFS tool needs mountpoint +After=initrd-root-fs.target +Before=initrd-switch-root.target +DefaultDependencies=no +IgnoreOnIsolate=true + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/bin/s3-grow-rootfs.sh diff --git a/modules.d/slx-drm/hooks/activate-nvidia-drivers.sh b/modules.d/slx-drm/hooks/activate-nvidia-drivers.sh deleted file mode 100644 index 00c2334a..00000000 --- a/modules.d/slx-drm/hooks/activate-nvidia-drivers.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash - -type emergency_shell >/dev/null 2>&1 || . /lib/dracut-lib.sh - -detect_nvidia_cards() { - # hard check on nvidia graphic cards - local cards="$( lspci -n | grep -o ' 03..: 10de:....' | awk '{print $2}' )" - if ! [ -d "/drm.cfg.d" ] && [ -n "$cards" ]; then - warn "Failed to find '/drm.cfg.d', but have nvidia cards - will try nouveau." - return 1 - fi - for card in $cards; do - local driver="$(awk '$1 = /'"$card"'/ {print $2; exit}' /drm.cfg.d/*)" - [ -z "$driver" ] && continue - driver="${driver#'@'}" - driver="${driver//-/\/}" - local driver_dir="/lib/modules/${driver}" - [ -d "$driver_dir" ] || continue - local driver_target="/lib/modules/$(uname -r)/kernel/drivers/gpu/drm/nvidia" - if [ -d "$driver_target" ]; then - warn "'$driver_target' exists, will not overwrite!" - return 1 - fi - # all good, move it over - if ! mv "$driver_dir" "$driver_target" 2>&1; then - warn "Failed to move '$driver_dir' to '$driver_target'." - return 1 - fi - # finally run depmod to make it visible to udev - if ! depmod -a 2>&1 ; then - warn "Failed to run depmod, udev won't see the nvidia modules." - return 1 - fi - # blacklist nouveau - echo 'blacklist nouveau' > "/lib/modprobe.d/disable-nouveau.conf" - info "Initialized nvidia drivers." - return 0 - done -} - -detect_nvidia_cards -: diff --git a/modules.d/slx-drm/hooks/copy-nvidia-drivers.sh b/modules.d/slx-drm/hooks/copy-nvidia-drivers.sh deleted file mode 100644 index 6e4a8529..00000000 --- a/modules.d/slx-drm/hooks/copy-nvidia-drivers.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/ash -# -# This script checks whether the nvidia kernel module was loaded by udev -# and copies the kernel modules over to stage4 and disables nouveau - -type emergency_shell >/dev/null 2>&1 || . /lib/dracut-lib.sh - -copy_nvidia_modules() { - local nvidia_moddir="/lib/modules/$(uname -r)/kernel/drivers/gpu/drm/nvidia" - if [ -d "${NEWROOT}/${nvidia_moddir}" ]; then - warn "Stage4 contains nvidia driver which would be overwritten - skipping." - return 1 - fi - if ! ( cp -r "$nvidia_moddir" "${NEWROOT}/${nvidia_moddir}" \ - && depmod -a -b "$NEWROOT" ); then - warn "Failed to copy/depmod nvidia modules to stage4." - return 1 - fi - # nouveau driver would needlessly load, prevent that - mkdir -p "${NEWROOT}/etc/modprobe.d" # cause why not - echo "blacklist nouveau" > "${NEWROOT}/etc/modprobe.d/disable-nouveau.conf" - return 0 -} - -if lsmod | grep -q '^nvidia'; then - copy_nvidia_modules -fi -: # fake success diff --git a/modules.d/slx-drm/hooks/s3-activate-nvidia-drivers.sh b/modules.d/slx-drm/hooks/s3-activate-nvidia-drivers.sh new file mode 100755 index 00000000..00c2334a --- /dev/null +++ b/modules.d/slx-drm/hooks/s3-activate-nvidia-drivers.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +type emergency_shell >/dev/null 2>&1 || . /lib/dracut-lib.sh + +detect_nvidia_cards() { + # hard check on nvidia graphic cards + local cards="$( lspci -n | grep -o ' 03..: 10de:....' | awk '{print $2}' )" + if ! [ -d "/drm.cfg.d" ] && [ -n "$cards" ]; then + warn "Failed to find '/drm.cfg.d', but have nvidia cards - will try nouveau." + return 1 + fi + for card in $cards; do + local driver="$(awk '$1 = /'"$card"'/ {print $2; exit}' /drm.cfg.d/*)" + [ -z "$driver" ] && continue + driver="${driver#'@'}" + driver="${driver//-/\/}" + local driver_dir="/lib/modules/${driver}" + [ -d "$driver_dir" ] || continue + local driver_target="/lib/modules/$(uname -r)/kernel/drivers/gpu/drm/nvidia" + if [ -d "$driver_target" ]; then + warn "'$driver_target' exists, will not overwrite!" + return 1 + fi + # all good, move it over + if ! mv "$driver_dir" "$driver_target" 2>&1; then + warn "Failed to move '$driver_dir' to '$driver_target'." + return 1 + fi + # finally run depmod to make it visible to udev + if ! depmod -a 2>&1 ; then + warn "Failed to run depmod, udev won't see the nvidia modules." + return 1 + fi + # blacklist nouveau + echo 'blacklist nouveau' > "/lib/modprobe.d/disable-nouveau.conf" + info "Initialized nvidia drivers." + return 0 + done +} + +detect_nvidia_cards +: diff --git a/modules.d/slx-drm/hooks/s3-copy-nvidia-drivers.sh b/modules.d/slx-drm/hooks/s3-copy-nvidia-drivers.sh new file mode 100755 index 00000000..5afc71c7 --- /dev/null +++ b/modules.d/slx-drm/hooks/s3-copy-nvidia-drivers.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# +# This script checks whether the nvidia kernel module was loaded by udev +# and copies the kernel modules over to stage4 and disables nouveau + +type emergency_shell >/dev/null 2>&1 || . /lib/dracut-lib.sh + +copy_nvidia_modules() { + local nvidia_moddir="/lib/modules/$(uname -r)/kernel/drivers/gpu/drm/nvidia" + if [ -d "${NEWROOT}/${nvidia_moddir}" ]; then + warn "Stage4 contains nvidia driver which would be overwritten - skipping." + return 1 + fi + if ! ( cp -r "$nvidia_moddir" "${NEWROOT}/${nvidia_moddir}" \ + && depmod -a -b "$NEWROOT" ); then + warn "Failed to copy/depmod nvidia modules to stage4." + return 1 + fi + # nouveau driver would needlessly load, prevent that + mkdir -p "${NEWROOT}/etc/modprobe.d" # cause why not + echo "blacklist nouveau" > "${NEWROOT}/etc/modprobe.d/disable-nouveau.conf" + return 0 +} + +if lsmod | grep -q '^nvidia'; then + copy_nvidia_modules +fi +: # fake success diff --git a/modules.d/slx-drm/module-setup.sh b/modules.d/slx-drm/module-setup.sh index 4baa5b2a..00ff80a7 100755 --- a/modules.d/slx-drm/module-setup.sh +++ b/modules.d/slx-drm/module-setup.sh @@ -17,6 +17,14 @@ install() { mkdir -p "${initdir}/${dir%/*}" cp -ar "$dir" "${initdir}/${dir%/*}" done - inst_hook pre-udev 10 "${moddir}/hooks/activate-nvidia-drivers.sh" - inst_hook pre-pivot 50 "${moddir}/hooks/copy-nvidia-drivers.sh" + for _name in "s3-activate-nvidia-drivers" "s3-copy-nvidia-drivers"; do + inst "$moddir/hooks/${_name}.sh" \ + "/usr/local/bin/${_name}.sh" + inst_simple "${moddir}/services/${_name}.service" \ + "${systemdsystemunitdir}/${_name}.service" + mkdir --parents \ + "${initdir}/${systemdsystemunitdir}/dracut-pre-pivot.service.requires" + ln_r "${systemdsystemunitdir}/${_name}.service" \ + "${systemdsystemunitdir}/dracut-pre-pivot.service.requires/${_name}.service" + done } diff --git a/modules.d/slx-drm/services/s3-activate-nvidia-drivers.service b/modules.d/slx-drm/services/s3-activate-nvidia-drivers.service new file mode 100644 index 00000000..4ea86525 --- /dev/null +++ b/modules.d/slx-drm/services/s3-activate-nvidia-drivers.service @@ -0,0 +1,13 @@ +[Unit] +Description=Enable loading of nVidia drivers if a suitable GPU is found +Requires=dracut-cmdline.service +After=dracut-cmdline.service +Before=dracut-pre-udev.service +Wants=dracut-pre-udev.service +DefaultDependencies=no +IgnoreOnIsolate=true + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/bin/s3-activate-nvidia-drivers.sh diff --git a/modules.d/slx-drm/services/s3-copy-nvidia-drivers.service b/modules.d/slx-drm/services/s3-copy-nvidia-drivers.service new file mode 100644 index 00000000..587a1175 --- /dev/null +++ b/modules.d/slx-drm/services/s3-copy-nvidia-drivers.service @@ -0,0 +1,12 @@ +[Unit] +Description=Copy nVidia drivers to stage 4 if in use +After=dracut-mount.service +After=s3-activate-nvidia-drivers.service +Before=initrd-switch-root.target +DefaultDependencies=no +IgnoreOnIsolate=true + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/bin/s3-copy-nvidia-drivers.sh diff --git a/modules.d/slx-network/hooks/activate-bootif-dhcp.sh b/modules.d/slx-network/hooks/activate-bootif-dhcp.sh deleted file mode 100644 index 9a5f0b88..00000000 --- a/modules.d/slx-network/hooks/activate-bootif-dhcp.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -# -# This script enables the udhcpc-based DHCP service for stage4, -# unless specifically disabled by 'SLX_STAGE4_DHCP="no"', -# or the OpenSLX udhcpc service/script exists in stage4. - -. /etc/openslx - -if [ "$SLX_STAGE4_DHCP" != "no" ] \ - && ! [ -e "${NEWROOT}/opt/openslx/scripts/udhcpc-openslx" ]; then - mkdir -p "${NEWROOT}/opt/openslx/scripts" - for script in setup-bootif-network udhcpc-trigger; do - if [ -e "${NEWROOT}/opt/openslx/scripts/${script}" ]; then - mv "${NEWROOT}/opt/openslx/scripts/${script}"{,.stage4} - fi - cp -f "/opt/openslx/scripts/${script}.stage4" \ - "${NEWROOT}/opt/openslx/scripts/${script}" - done - - # copy udhcpc@ systemd service, backup existing ones for debugging - mkdir -p "${NEWROOT}/etc/systemd/system" - cp -f "/opt/openslx/services/udhcpc-bootif.service" "${NEWROOT}/etc/systemd/system" - - # it requires /run/network - echo 'd /run/network 0755 root root' > "${NEWROOT}/etc/tmpfiles.d/network.conf" - - # activate it for the bridge or the physical interface if not bridged - systemctl --quiet --root "$NEWROOT" enable "udhcpc-bootif" -fi - -: diff --git a/modules.d/slx-network/hooks/activate-stage4-dhcp.sh b/modules.d/slx-network/hooks/activate-stage4-dhcp.sh new file mode 100755 index 00000000..8ac1e7b0 --- /dev/null +++ b/modules.d/slx-network/hooks/activate-stage4-dhcp.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# +# This script enables the udhcpc-based DHCP service for stage4, +# unless specifically disabled by 'SLX_STAGE4_DHCP="no"', +# or the OpenSLX udhcpc service/script exists in stage4. + +. /etc/openslx + +if [ "$SLX_STAGE4_DHCP" != "no" ] \ + && ! [ -e "${NEWROOT}/opt/openslx/scripts/udhcpc-openslx" ]; then + mkdir -p "${NEWROOT}/opt/openslx/scripts" + for script in s3-setup-bootif-network udhcpc-trigger; do + if [ -e "${NEWROOT}/opt/openslx/scripts/${script}" ]; then + mv "${NEWROOT}/opt/openslx/scripts/${script}"{,.stage4} + fi + cp -f "/opt/openslx/scripts/${script}.stage4" \ + "${NEWROOT}/opt/openslx/scripts/${script}" + done + + # copy udhcpc@ systemd service, backup existing ones for debugging + mkdir -p "${NEWROOT}/etc/systemd/system" + cp -f "/opt/openslx/services/udhcpc-bootif.service" "${NEWROOT}/etc/systemd/system" + + # it requires /run/network + echo 'd /run/network 0755 root root' > "${NEWROOT}/etc/tmpfiles.d/network.conf" + + # activate it for the bridge or the physical interface if not bridged + systemctl --quiet --root "$NEWROOT" enable "udhcpc-bootif" +fi + +: diff --git a/modules.d/slx-network/hooks/configure-jumbo-frames.sh b/modules.d/slx-network/hooks/configure-jumbo-frames.sh old mode 100644 new mode 100755 diff --git a/modules.d/slx-network/hooks/copy-network-files.sh b/modules.d/slx-network/hooks/copy-network-files.sh deleted file mode 100644 index 5d28669e..00000000 --- a/modules.d/slx-network/hooks/copy-network-files.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -type -p warn &> /dev/null || . /lib/dracut-lib.sh - -if [ -n "$NEWROOT" ]; then - # backup network configuration files found in stage4 - for file in /etc/{hostname,hosts,resolv.conf}; do - if [ ! -e "$file" ]; then - warn "Missing '$file' - can't move it to stage4. " - continue - fi - if [ -e "${NEWROOT}/${file}" ] || [ -h "${NEWROOT}/${file}" ]; then - mv "${NEWROOT}/${file}" "${NEWROOT}/${file}.renamed-by-stage3" - fi - cp -af "$file" "${NEWROOT}/etc/" - done - # special handling for resolv.conf: - # move it to /opt/openslx to detect we are managing it - mkdir -p "${NEWROOT}/opt/openslx" - mv "${NEWROOT}/etc/resolv.conf" "${NEWROOT}/opt/openslx/resolv.conf" - ln -s "/opt/openslx/resolv.conf" "${NEWROOT}/etc/resolv.conf" - - # HACK: finally make sure we have rdns helper - # This should be done more elegantly one day... - rdns="$(type -p rdns)" - if [ -n "$rdns" ]; then - mkdir -p "${NEWROOT}/opt/openslx/bin" - cp -f "$rdns" "${NEWROOT}/opt/openslx/bin" - fi -fi -true diff --git a/modules.d/slx-network/hooks/parse-ipxe-network-kcl.sh b/modules.d/slx-network/hooks/parse-ipxe-network-kcl.sh deleted file mode 100644 index fec160a8..00000000 --- a/modules.d/slx-network/hooks/parse-ipxe-network-kcl.sh +++ /dev/null @@ -1,117 +0,0 @@ -#!/bin/bash -# -# Script parsing the kernel command line to get the IP configuration from -# (i)PXE. Supports either the older -# ip=::: format or our new format -# consisting of 'ipv4.' key/value pairs. Valid attributes are: ip, router, -# dns, hostname, domain, search, if, ntpsrv, subnet. - -command -v getarg >/dev/null || . /lib/dracut-lib.sh - -# static names for the boot interface and its bridge -declare -rg bootif_name="boot0" -declare -rg bridge_name="br0" - -# parse old syslinux style 'ip=...' and 'BOOTIF=...' parameters -parse_kcl_ip() { - declare -g if="$(getarg BOOTIF=)" - if [ -n "$if" ] && [ ${#if} -eq 20 ]; then - if="${if#???}" - if="${if//-/:}" - fi - local ip_line="$(getarg ip=)" - [ -z "$ip_line" ] && return 0 - read -r ip bootsrv router subnet \ - <<< $( awk -F: '{print $1" "$2" "$3" "$4}' <<< "${ip_line}" ) - declare -g ip bootsrv router subnet -} - -# parse new style 'ipv4.*=...' parameters -parse_kcl_ipv4() { - for param in ip router dns hostname domain search if ntpsrv subnet; do - echo "Getting $param..." - local current="$(getarg ipv4.${param}=)" - [ -z "$current" ] && continue - declare -g "${param}=${current}" - done -} - -parse_kcl() { - # we assume (and we should) that both variants contain the - # same information if they are present simultaneously. - parse_kcl_ip - parse_kcl_ipv4 - - # if not boot server was given, use slxsrv - if [ -z "$bootsrv" ]; then - kclsrv="$(getarg slxsrv=)" - if [ -n "$kclsrv" ]; then - declare -g bootsrv="$kclsrv" - fi - fi - - # calculate network mask - declare -g mask="$(ipcalc -s -p "$ip" "$subnet" | sed 's/.*=//')" - - # vlan specified? - declare -g vlan="$(getarg vlan=)" - - # Bridged the boot interface? - grep -wqE 'bridged' /proc/cmdline && declare -g bridged="y" - - # backwards compat for old style hostname/dns/domain - for conf in hostname dns domain; do - conf_value="$(getarg ${conf}=)" - if [ -n "$conf_value" ]; then - declare -g "${conf}=${conf_value}" - fi - done -} - -save_network_config() { - declare -rg network_conf="/run/openslx/network.conf" - mkdir -p "${network_conf%/*}" - cat <<-EOF > "$network_conf" - SLX_PXE_CLIENT_IP='$ip' - SLX_PXE_SERVER_IP='$bootsrv' - SLX_PXE_GATEWAY='$router' - SLX_PXE_NETMASK='$mask' - SLX_PXE_SUBNET='$subnet' - SLX_PXE_MAC='$if' - SLX_PXE_NETIF='$bootif_name' - SLX_PXE_DNS='${dns//,/ }' - SLX_PXE_HOSTNAME='$hostname' - SLX_PXE_DOMAIN='${search//,/ }' - SLX_PXE_NTP='${ntpsrv//,/ }' - SLX_BRIDGE='${bridged:+${bridge_name}}' - SLX_VLAN_ID='$vlan' - EOF -} - -# Create udev rule to rename the PXE boot interface to BOOTIF_NAME -create_udev_bootif_name_rule() { - if [ -z "$if" ]; then - echo "MAC address of boot interface not set!" - return 1 - fi - # priority 70 < 80-net-name-slot.rules. - echo 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="'$if'", NAME="'$bootif_name'"' > /etc/udev/rules.d/70-pxe-boot-interface.rules -} - -## MAIN ## -parse_kcl - -# set hostname asap -[ -n "$hostname" ] && echo "$hostname" > /proc/sys/kernel/hostname - -# Save network config for later use -save_network_config & - -# Create the udev rule to rename the boot interface to the declared BOOTIF_NAME -create_udev_bootif_name_rule & - -wait - -# TODO handle case where the MAC address of the boot interface was not found -/sbin/initqueue --settled /usr/local/bin/setup-bootif-network -/sbin/initqueue --finished [ -e "/.network" ] diff --git a/modules.d/slx-network/hooks/s3-copy-network-files.sh b/modules.d/slx-network/hooks/s3-copy-network-files.sh new file mode 100755 index 00000000..5d28669e --- /dev/null +++ b/modules.d/slx-network/hooks/s3-copy-network-files.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +type -p warn &> /dev/null || . /lib/dracut-lib.sh + +if [ -n "$NEWROOT" ]; then + # backup network configuration files found in stage4 + for file in /etc/{hostname,hosts,resolv.conf}; do + if [ ! -e "$file" ]; then + warn "Missing '$file' - can't move it to stage4. " + continue + fi + if [ -e "${NEWROOT}/${file}" ] || [ -h "${NEWROOT}/${file}" ]; then + mv "${NEWROOT}/${file}" "${NEWROOT}/${file}.renamed-by-stage3" + fi + cp -af "$file" "${NEWROOT}/etc/" + done + # special handling for resolv.conf: + # move it to /opt/openslx to detect we are managing it + mkdir -p "${NEWROOT}/opt/openslx" + mv "${NEWROOT}/etc/resolv.conf" "${NEWROOT}/opt/openslx/resolv.conf" + ln -s "/opt/openslx/resolv.conf" "${NEWROOT}/etc/resolv.conf" + + # HACK: finally make sure we have rdns helper + # This should be done more elegantly one day... + rdns="$(type -p rdns)" + if [ -n "$rdns" ]; then + mkdir -p "${NEWROOT}/opt/openslx/bin" + cp -f "$rdns" "${NEWROOT}/opt/openslx/bin" + fi +fi +true diff --git a/modules.d/slx-network/hooks/s3-parse-network-kcl.sh b/modules.d/slx-network/hooks/s3-parse-network-kcl.sh new file mode 100755 index 00000000..daf2e9b7 --- /dev/null +++ b/modules.d/slx-network/hooks/s3-parse-network-kcl.sh @@ -0,0 +1,119 @@ +#!/bin/bash +# +# Script parsing the kernel command line to get the IP configuration from +# (i)PXE. Supports either the older +# ip=::: format or our new format +# consisting of 'ipv4.' key/value pairs. Valid attributes are: ip, router, +# dns, hostname, domain, search, if, ntpsrv, subnet. + +command -v getarg >/dev/null || . /lib/dracut-lib.sh + +# static names for the boot interface and its bridge +declare -rg bootif_name="boot0" +declare -rg bridge_name="br0" + +# parse old syslinux style 'ip=...' and 'BOOTIF=...' parameters +parse_kcl_ip() { + declare -g if="$(getarg BOOTIF=)" + if [ -n "$if" ] && [ ${#if} -eq 20 ]; then + if="${if#???}" + if="${if//-/:}" + fi + local ip_line="$(getarg ip=)" + [ -z "$ip_line" ] && return 0 + read -r ip bootsrv router subnet \ + <<< $( awk -F: '{print $1" "$2" "$3" "$4}' <<< "${ip_line}" ) + declare -g ip bootsrv router subnet +} + +# parse new style 'ipv4.*=...' parameters +parse_kcl_ipv4() { + for param in "ip" "router" "dns" "hostname" "domain" "search" "if" "ntpsrv" "subnet"; do + local current="$(getarg ipv4.${param}=)" + echo "KCL '$param'='$current'" + [ -z "$current" ] && continue + declare -g "${param}=${current}" + done +} + +parse_kcl() { + # we assume (and we should) that both variants contain the + # same information if they are present simultaneously. + parse_kcl_ip + parse_kcl_ipv4 + + # if not boot server was given, use slxsrv + if [ -z "$bootsrv" ]; then + kclsrv="$(getarg slxsrv=)" + if [ -n "$kclsrv" ]; then + declare -g bootsrv="$kclsrv" + fi + fi + + # calculate network mask + declare -g mask="$(ipcalc -s -p "$ip" "$subnet" | sed 's/.*=//')" + + # vlan specified? + declare -g vlan="$(getarg vlan=)" + + # Bridged the boot interface? + grep -wqE 'bridged' /proc/cmdline && declare -g bridged="y" + + # backwards compat for old style hostname/dns/domain + for conf in hostname dns domain; do + conf_value="$(getarg ${conf}=)" + if [ -n "$conf_value" ]; then + declare -g "${conf}=${conf_value}" + fi + done +} + +save_network_config() { + declare -rg network_conf="/run/openslx/network.conf" + mkdir -p "${network_conf%/*}" + cat <<-EOF > "$network_conf" + SLX_PXE_CLIENT_IP='$ip' + SLX_PXE_SERVER_IP='$bootsrv' + SLX_PXE_GATEWAY='$router' + SLX_PXE_NETMASK='$mask' + SLX_PXE_SUBNET='$subnet' + SLX_PXE_MAC='$if' + SLX_PXE_NETIF='$bootif_name' + SLX_PXE_DNS='${dns//,/ }' + SLX_PXE_HOSTNAME='$hostname' + SLX_PXE_DOMAIN='${search//,/ }' + SLX_PXE_NTP='${ntpsrv//,/ }' + SLX_BRIDGE='${bridged:+${bridge_name}}' + SLX_VLAN_ID='$vlan' + EOF + echo "network.conf written." +} + +# Create udev rule to rename the PXE boot interface to BOOTIF_NAME +create_udev_bootif_name_rule() { + if [ -z "$if" ]; then + echo "MAC address of boot interface not set!" + return 1 + fi + # priority 70 < 80-net-name-slot.rules. + echo 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="'$if'", NAME="'$bootif_name'"' > /etc/udev/rules.d/70-pxe-boot-interface.rules +} + +## MAIN ## +parse_kcl + +if [ -z "$if" ]; then + emergency_shell "MAC address of primary interface not given on command line" + exit 1 +fi + +# Create the udev rule to rename the boot interface to the declared BOOTIF_NAME +create_udev_bootif_name_rule & + +# Save network config for later use +save_network_config & + +# set hostname asap +[ -n "$hostname" ] && echo "$hostname" > /proc/sys/kernel/hostname + +wait diff --git a/modules.d/slx-network/hooks/s3-setup-bootif-network.sh b/modules.d/slx-network/hooks/s3-setup-bootif-network.sh new file mode 100755 index 00000000..1ae58464 --- /dev/null +++ b/modules.d/slx-network/hooks/s3-setup-bootif-network.sh @@ -0,0 +1,186 @@ +#!/bin/bash +# +# This script sets up the main network interface we booted from, +# as designated by SLX_PXE_NETIF (parsed from the PXE KCL in the +# cmdline dracut hook). +# It is run inside dracut's initqueue, on settles to detect the +# physical network interface as fast as possible. + +type emergency_shell >/dev/null 2>&1 || . /lib/dracut-lib.sh + +. /run/openslx/network.conf + +# do not run until the physical interface exists (driver loaded) +_fails=0 +while ! [ -e "/sys/class/net/${SLX_PXE_NETIF}/device" ]; do + if (( ++_fails > 20 )); then + emergency_shell "Boot interface '${SLX_PXE_NETIF}' did not appear" + exit 1 + fi + (( (_fails % 5) == 0 )) && udevadm trigger + usleep 500000 +done + +# wrapper around splashtool to disable it if its not present +_splashtool() { + : +} +if grep -wq splash /proc/cmdline && hash splashtool &> /dev/null; then + _splashtool() { + splashtool "$@" + } +fi + +wait_for_iface() { + local _iface="$1" + local _timeout="${2:-50}" + local _fails=0 + local _state + if [ "$_iface" = "$SLX_PXE_NETIF" ]; then + img="??-nic" + elif [ "$_iface" = "$SLX_BRIDGE" ]; then + img="??-bridge" + fi + while (( _timeout-- > 0 )); do + _state="$(cat "/sys/class/net/${_iface}/operstate" 2> /dev/null)" + [ "$_state" = "up" ] && break + if (( ++_fails > 5 )) && [[ "$_state" = "unknown" || "$_state" = "" ]]; then + if (( _fails > 10 )) || [ "$(cat "/sys/class/net/${_iface}/carrier" 2> /dev/null)" = "1" ]; then + break + fi + fi + # every 500ms + usleep 500000 + in= + (( (_timeout % 2) == 0 )) && in=in + _splashtool --icon "/opt/openslx/icons/${in}active/${img}.ppm" & + done + if (( _timeout > 0 )); then + _splashtool --icon "/opt/openslx/icons/active/${img}.ppm" & + return 0 + else + _splashtool --icon "/opt/openslx/icons/inactive/${img}.ppm" & + return 1 + fi +} + +# For debugging... +{ +set -x + +ip link set dev "$SLX_PXE_NETIF" up +if ! wait_for_iface "$SLX_PXE_NETIF" 60; then + warn "'$SLX_PXE_NETIF' still not up after 30sec ... trying anyway." + # TODO handle case where we waited for 30sec and it is still not up +fi + +# now determine whether we are in bridged/vlan/plain mode +MAIN_NETIF="$SLX_PXE_NETIF" +if [ -n "$SLX_VLAN_ID" ]; then + # create VLAN interface + modprobe 8021q || warn "Loading '8021q' failed - missing module?" + ip link add link "$SLX_PXE_NETIF" name "${SLX_PXE_NETIF}.${SLX_VLAN_ID}" \ + type vlan id "$SLX_VLAN_ID" + ip link set dev "${SLX_PXE_NETIF}.${SLX_VLAN_ID}" up + if wait_for_iface "${SLX_PXE_NETIF}.${SLX_VLAN_ID}"; then + MAIN_NETIF="${SLX_PXE_NETIF}.${SLX_VLAN_ID}" + else + warn "Setting up VLAN '$SLX_VLAN_ID' failed, trying plain..." + fi +fi + +if [ -n "$SLX_BRIDGE" ]; then + for try in {1..10} ""; do + if ( + set -e + brctl addbr "$SLX_BRIDGE" + brctl stp "$SLX_BRIDGE" 0 + brctl setfd "$SLX_BRIDGE" 0.000000000001 + ip link set addr "$SLX_PXE_MAC" "$SLX_BRIDGE" + brctl addif "$SLX_BRIDGE" "$MAIN_NETIF" + ip link set dev "$SLX_BRIDGE" up + wait_for_iface "$SLX_BRIDGE" + ); then + MAIN_NETIF="$SLX_BRIDGE" + break + fi + + # nope, handle + if [ -z "$try" ]; then + emergency_shell "Failed to setup main network bridge, giving up!" + exit 1 + fi + warn "Failed to setup main network bridge on try $try. Retrying ..." + # delete bridge, inc try and sleep 100ms before trying again + [ -e "/sys/class/net/${SLX_BRIDGE}" ] && brctl delbr "$SLX_BRIDGE" + usleep 100000 + done +fi + +# Finally add the IP address on the main NIC. +# Note: we should never have ip & netmask without the other. +if [ -n "$SLX_PXE_CLIENT_IP" ] && [ -n "$SLX_PXE_NETMASK" ]; then + ip addr add "${SLX_PXE_CLIENT_IP}/${SLX_PXE_NETMASK}" dev "$MAIN_NETIF" + + # now if we got everything from the KCL and skip dhcp if so + if [ -n "$SLX_PXE_GATEWAY" ] && [ -n "$SLX_PXE_DNS" ]; then + info "Got network configuration from KCL, skipping DHCP." + if ! interface="$MAIN_NETIF" \ + ip="$SLX_PXE_CLIENT_IP" \ + router="$SLX_PXE_GATEWAY" \ + dns="$SLX_PXE_DNS" \ + hostname="$SLX_PXE_HOSTNAME" \ + /usr/local/bin/udhcpc-trigger bound; then + warn "Failed to launch DHCP trigger with KCL configuration - will DHCP." + fi + exit 0 + fi +fi + +## DHCP +# Try to work around some buggy e1000e variants by waiting a bit +sleep 1 +additional_opts=() + +# we need to send the same UID (without '-') as the PXE firmware did, so use the plain +# one read with dmidecode (and not the one by s3-get-system-uuid). +if [ "$USE_DHCP_UUID" = "yes" ]; then + UID="$(dmidecode -s system-uuid | sed -r 's/^(..)(..)(..)(..)-(..)(..)-(..)(..)-(....)-/00\4\3\2\1\6\5\8\7\9/')" + if [ "${#UID}" = 34 ]; then + echo "Using SMBIOS UID for DHCP" + additional_opts+=("-x" "0x3d:$UID") + fi +fi + +if [ -n "$SLX_PXE_CLIENT_IP" ]; then + additional_opts+=("-r" "$SLX_PXE_CLIENT_IP") +fi + +# DHCP options to request +request_opts=("-O" "hostname") +request_opts+=("-O" "dns") +request_opts+=("-O" "ntpsrv") +request_opts+=("-O" "domain") +request_opts+=("-O" "wpad") +request_opts+=("-O" "search") +request_opts+=("-O" "nisdomain") + +# udhcpc +for i in 1 1 1 fail; do + if [ "$i" = "fail" ]; then + emergency_shell "DHCP failed 3 times... cannot continue." + exit 1 + fi + udhcpc -t 4 -T 3 -f -n -q \ + -i "${MAIN_NETIF}" \ + "${request_opts[@]}" \ + "${additional_opts[@]}" \ + -s "/usr/local/bin/udhcpc-trigger" \ + && break + # failed, keep trying... + warn "DHCP failed, retrying in $i sec..." + sleep $i +done + +set +x +} {BASH_XTRACEFD}> "/run/openslx/initramfs-network.log.$$" diff --git a/modules.d/slx-network/module-setup.sh b/modules.d/slx-network/module-setup.sh index bf384322..6e28ba1f 100755 --- a/modules.d/slx-network/module-setup.sh +++ b/modules.d/slx-network/module-setup.sh @@ -24,19 +24,48 @@ install() { inst_libdir_file {"tls/$_arch/",tls/,"$_arch/",}"libnss_dns.so.*" # stage3 network scripts - inst "${moddir}/scripts/setup-bootif-network.stage3" "/usr/local/bin/setup-bootif-network" inst "${moddir}/scripts/udhcpc-trigger.stage3" "/usr/local/bin/udhcpc-trigger" # files for stage4, park them in /opt/openslx gcc -o "${initdir}/usr/local/bin/rdns" "${moddir}/rdns.c" - inst "${moddir}/scripts/setup-bootif-network.stage4" "/opt/openslx/scripts/setup-bootif-network.stage4" + inst "${moddir}/scripts/s3-setup-bootif-network.stage4" "/opt/openslx/scripts/s3-setup-bootif-network.stage4" inst "${moddir}/scripts/udhcpc-trigger.stage4" "/opt/openslx/scripts/udhcpc-trigger.stage4" - inst "${moddir}/services/udhcpc-bootif.service" "/opt/openslx/services/udhcpc-bootif.service" + inst "${moddir}/services/udhcpc-bootif-stage4.service" "/opt/openslx/services/udhcpc-bootif.service" + + # Parse IP config etc. from KCL + _name="s3-parse-network-kcl" + inst "$moddir/hooks/${_name}.sh" \ + "/usr/local/bin/${_name}.sh" + inst_simple "${moddir}/services/${_name}.service" \ + "${systemdsystemunitdir}/${_name}.service" + mkdir --parents \ + "${initdir}/${systemdsystemunitdir}/dracut-cmdline.service.requires" + ln_r "${systemdsystemunitdir}/${_name}.service" \ + "${systemdsystemunitdir}/dracut-cmdline.service.requires/${_name}.service" + # Setup bootif network: bridge, vlan, dhcp, ... + _name="s3-setup-bootif-network" + inst "$moddir/hooks/${_name}.sh" \ + "/usr/local/bin/${_name}.sh" + inst_simple "${moddir}/services/${_name}.service" \ + "${systemdsystemunitdir}/${_name}.service" + mkdir --parents \ + "${initdir}/${systemdsystemunitdir}/dracut-initqueue.service.wants" + ln_r "${systemdsystemunitdir}/${_name}.service" \ + "${systemdsystemunitdir}/dracut-initqueue.service.wants/${_name}.service" + # Copy files related to networking. Must be the very last thing before switchroot to + # make sure nothing else tries to meddle with these after copying, e.g. slx-ssl. + _name="s3-copy-network-files" + inst "$moddir/hooks/${_name}.sh" \ + "/usr/local/bin/${_name}.sh" + inst_simple "${moddir}/services/${_name}.service" \ + "${systemdsystemunitdir}/${_name}.service" + mkdir --parents \ + "${initdir}/${systemdsystemunitdir}/dracut-pre-pivot.service.requires" + ln_r "${systemdsystemunitdir}/${_name}.service" \ + "${systemdsystemunitdir}/dracut-pre-pivot.service.requires/${_name}.service" # hooks - inst_hook cmdline 10 "${moddir}/hooks/parse-ipxe-network-kcl.sh" inst_hook pre-pivot 10 "${moddir}/hooks/configure-jumbo-frames.sh" # DO NOT TOUCH THIS - inst_hook pre-pivot 99 "${moddir}/hooks/copy-network-files.sh" - inst_hook pre-pivot 60 "${moddir}/hooks/activate-bootif-dhcp.sh" + inst_hook pre-pivot 60 "${moddir}/hooks/activate-stage4-dhcp.sh" } diff --git a/modules.d/slx-network/scripts/setup-bootif-network.stage3 b/modules.d/slx-network/scripts/setup-bootif-network.stage3 deleted file mode 100755 index 1f8fb9ee..00000000 --- a/modules.d/slx-network/scripts/setup-bootif-network.stage3 +++ /dev/null @@ -1,172 +0,0 @@ -#!/bin/bash -# -# This script sets up the main network interface we booted from, -# as designated by SLX_PXE_NETIF (parsed from the PXE KCL in the -# cmdline dracut hook). -# It is run inside dracut's initqueue, on settles to detect the -# physical network interface as fast as possible. - -type emergency_shell >/dev/null 2>&1 || . /lib/dracut-lib.sh - -. /run/openslx/network.conf - -# do not run until the physical interface is not ready yet -if [ ! -e "/sys/class/net/${SLX_PXE_NETIF}/device" ]; then - exit 1 -fi - -# wrapper around splashtool to disable it if its not present -_splashtool() { - : -} -if grep -wq splash /proc/cmdline && hash splashtool &> /dev/null; then - _splashtool() { - splashtool "$@" - } -fi - -wait_for_iface() { - local _iface="$1" - local _timeout="${2:-50}" - if [ "$_iface" = "$SLX_PXE_NETIF" ]; then - img="??-nic" - elif [ "$_iface" = "$SLX_BRIDGE" ]; then - img="??-bridge" - fi - while [ "$_timeout" -ne 0 ]; do - [ "$(cat /sys/class/net/${_iface}/operstate)" = "up" ] && break - (( _timeout -- )) - # every 500ms - usleep 500000 - in= - [ "$(( _timeout % 2 ))" = 0 ] && in=in - _splashtool --icon "/opt/openslx/icons/${in}active/${img}.ppm" & - done - if [ "$_timeout" -ne 0 ]; then - _splashtool --icon "/opt/openslx/icons/active/${img}.ppm" & - return 0 - else - _splashtool --icon "/opt/openslx/icons/inactive/${img}.ppm" & - return 1 - fi -} - -# For debugging... -{ -set -x - -ip link set dev "$SLX_PXE_NETIF" up -if ! wait_for_iface "$SLX_PXE_NETIF" 60; then - warn "'$SLX_PXE_NETIF' still not up after 30sec ... trying anyway." - # TODO handle case where we waited for 30sec and it is still not up -fi - -# now determine whether we are in bridged/vlan/plain mode -MAIN_NETIF="$SLX_PXE_NETIF" -if [ -n "$SLX_VLAN_ID" ]; then - # create VLAN interface - modprobe 8021q || warn "Loading '8021q' failed - missing module?" - ip link add link "$SLX_PXE_NETIF" name "${SLX_PXE_NETIF}.${SLX_VLAN_ID}" \ - type vlan id "$SLX_VLAN_ID" - ip link set dev "${SLX_PXE_NETIF}.${SLX_VLAN_ID}" up - if wait_for_iface "${SLX_PXE_NETIF}.${SLX_VLAN_ID}"; then - MAIN_NETIF="${SLX_PXE_NETIF}.${SLX_VLAN_ID}" - else - warn "Setting up VLAN '$SLX_VLAN_ID' failed, trying plain..." - fi -fi - -if [ -n "$SLX_BRIDGE" ]; then - for try in {1..10} ""; do - ( - set -e - brctl addbr "$SLX_BRIDGE" - brctl stp "$SLX_BRIDGE" 0 - brctl setfd "$SLX_BRIDGE" 0.000000000001 - ip link set addr "$SLX_PXE_MAC" "$SLX_BRIDGE" - brctl addif "$SLX_BRIDGE" "$MAIN_NETIF" - ip link set dev "$SLX_BRIDGE" up - wait_for_iface "$SLX_BRIDGE" - ) - # success? - if [ "$?" -eq 0 ]; then - MAIN_NETIF="$SLX_BRIDGE" - break - fi - - # nope, handle - if [ -z "$try" ]; then - emergency_shell "Failed to setup main network bridge, giving up!" - fi - warn "Failed to setup main network bridge on try $try. Retrying ..." - # delete bridge, inc try and sleep 100ms before trying again - [ -e "/sys/class/net/${SLX_BRIDGE}" ] && brctl delbr "$SLX_BRIDGE" - usleep 100000 - done -fi - -# Finally add the IP address on the main NIC. -# Note: we should never have ip & netmask without the other. -if [ -n "$SLX_PXE_CLIENT_IP" ] && [ -n "$SLX_PXE_NETMASK" ]; then - ip addr add "${SLX_PXE_CLIENT_IP}/${SLX_PXE_NETMASK}" dev "$MAIN_NETIF" - - # now if we got everything from the KCL and skip dhcp if so - if [ -n "$SLX_PXE_GATEWAY" ] && [ -n "$SLX_PXE_DNS" ]; then - info "Got network configuration from KCL, skipping DHCP." - if ! interface="$MAIN_NETIF" \ - ip="$SLX_PXE_CLIENT_IP" \ - router="$SLX_PXE_GATEWAY" \ - dns="$SLX_PXE_DNS" \ - hostname="$SLX_PXE_HOSTNAME" \ - /usr/local/bin/udhcpc-trigger bound; then - warn "Failed to launch DHCP trigger with KCL configuration - will DHCP." - fi - exit 0 - fi -fi - -## DHCP -# Try to work around some buggy e1000e variants by waiting a bit -sleep 1 -additional_opts=() - -# we need to send the same UID (without '-') as the PXE firmware did, so use the plain -# one read with dmidecode (and not the one by get-system-uuid). -if [ "$USE_DHCP_UUID" = "yes" ]; then - UID="$(dmidecode -s system-uuid | sed -r 's/^(..)(..)(..)(..)-(..)(..)-(..)(..)-(....)-/00\4\3\2\1\6\5\8\7\9/')" - if [ "${#UID}" = 34 ]; then - echo "Using SMBIOS UID for DHCP" - additional_opts+=("-x" "0x3d:$UID") - fi -fi - -if [ -n "$SLX_PXE_CLIENT_IP" ]; then - additional_opts+=("-r" "$SLX_PXE_CLIENT_IP") -fi - -# DHCP options to request -request_opts=("-O" "hostname") -request_opts+=("-O" "dns") -request_opts+=("-O" "ntpsrv") -request_opts+=("-O" "domain") -request_opts+=("-O" "wpad") -request_opts+=("-O" "search") -request_opts+=("-O" "nisdomain") - -# udhcpc -for i in 1 1 1 fail; do - [ "$i" = "fail" ] && emergency_shell "DHCP failed 3 times... cannot continue." - udhcpc -t 4 -T 3 -f -n -q \ - -i "${MAIN_NETIF}" \ - "${request_opts[@]}" \ - "${additional_opts[@]}" \ - -s "/usr/local/bin/udhcpc-trigger" - # success? - [ "$?" -eq 0 ] && break - # nope, keep trying... - warn "DHCP failed, retrying in 1sec..." - sleep $i -done - -set +x -} {BASH_XTRACEFD}> "/run/openslx/initramfs-network.log.$$" diff --git a/modules.d/slx-network/services/s3-copy-network-files.service b/modules.d/slx-network/services/s3-copy-network-files.service new file mode 100644 index 00000000..24325cef --- /dev/null +++ b/modules.d/slx-network/services/s3-copy-network-files.service @@ -0,0 +1,12 @@ +[Unit] +Description=Copy networking-related files to stage 4 +After=s3-ssl-sat.service +After=initrd-root-fs.target +Before=initrd-switch-root.target +DefaultDependencies=no +IgnoreOnIsolate=true + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/bin/s3-copy-network-files.sh diff --git a/modules.d/slx-network/services/s3-parse-network-kcl.service b/modules.d/slx-network/services/s3-parse-network-kcl.service new file mode 100644 index 00000000..da0445ca --- /dev/null +++ b/modules.d/slx-network/services/s3-parse-network-kcl.service @@ -0,0 +1,13 @@ +[Unit] +Description=Extract network config from KCL +DefaultDependencies=no +Wants=dracut-cmdline.service +After=dracut-cmdline.service +Wants=systemd-udevd.service +Before=systemd-udevd.service +IgnoreOnIsolate=true + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/bin/s3-parse-network-kcl.sh diff --git a/modules.d/slx-network/services/s3-setup-bootif-network.service b/modules.d/slx-network/services/s3-setup-bootif-network.service new file mode 100644 index 00000000..227a8ff6 --- /dev/null +++ b/modules.d/slx-network/services/s3-setup-bootif-network.service @@ -0,0 +1,13 @@ +[Unit] +Description=Configure main network link +DefaultDependencies=no +IgnoreOnIsolate=true +Wants=s3-parse-network-kcl.service +After=s3-parse-network-kcl.service +Before=dracut-pre-mount.service +Before=s3-dnbd3root.service + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/bin/s3-setup-bootif-network.sh diff --git a/modules.d/slx-network/services/udhcpc-bootif-stage4.service b/modules.d/slx-network/services/udhcpc-bootif-stage4.service new file mode 100644 index 00000000..71a721dc --- /dev/null +++ b/modules.d/slx-network/services/udhcpc-bootif-stage4.service @@ -0,0 +1,12 @@ +[Unit] +Description=DHCP Client for the main boot interface +Requires=systemd-tmpfiles-setup.service +After=systemd-tmpfiles-setup.service +DefaultDependencies=no + +[Service] +Type=forking +ExecStart=/opt/openslx/scripts/setup-bootif-network + +[Install] +WantedBy=sysinit.target diff --git a/modules.d/slx-network/services/udhcpc-bootif.service b/modules.d/slx-network/services/udhcpc-bootif.service deleted file mode 100644 index 71a721dc..00000000 --- a/modules.d/slx-network/services/udhcpc-bootif.service +++ /dev/null @@ -1,12 +0,0 @@ -[Unit] -Description=DHCP Client for the main boot interface -Requires=systemd-tmpfiles-setup.service -After=systemd-tmpfiles-setup.service -DefaultDependencies=no - -[Service] -Type=forking -ExecStart=/opt/openslx/scripts/setup-bootif-network - -[Install] -WantedBy=sysinit.target diff --git a/modules.d/slx-runmode/hooks/s3-enable-runmode.sh b/modules.d/slx-runmode/hooks/s3-enable-runmode.sh new file mode 100755 index 00000000..a563486f --- /dev/null +++ b/modules.d/slx-runmode/hooks/s3-enable-runmode.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# +# SLX_SYSTEMD_TARGET overrides the default.target to boot +# the stage4 into - required for the so-called 'run mode'. + +. /etc/openslx + +if [ -n "$SLX_SYSTEMD_TARGET" ]; then + SLX_SYSTEMD_TARGET="${SLX_SYSTEMD_TARGET%.target}.target" + ln -sf "$SLX_SYSTEMD_TARGET" "$NEWROOT/etc/systemd/system/default.target" +fi + +true diff --git a/modules.d/slx-runmode/module-setup.sh b/modules.d/slx-runmode/module-setup.sh index 7705ad06..c6ab9524 100755 --- a/modules.d/slx-runmode/module-setup.sh +++ b/modules.d/slx-runmode/module-setup.sh @@ -8,6 +8,13 @@ depends() { echo dnbd3-rootfs } install() { - # unpacking config.tgz should be pre-pivot 10, let's do this afterwards - inst_hook pre-pivot 50 "$moddir/scripts/runmode.sh" + _name="s3-enable-runmode" + inst "$moddir/hooks/${_name}.sh" \ + "/usr/local/bin/${_name}.sh" + inst_simple "${moddir}/services/${_name}.service" \ + "${systemdsystemunitdir}/${_name}.service" + mkdir --parents \ + "${initdir}/${systemdsystemunitdir}/dracut-pre-pivot.service.requires" + ln_r "${systemdsystemunitdir}/${_name}.service" \ + "${systemdsystemunitdir}/dracut-pre-pivot.service.requires/${_name}.service" } diff --git a/modules.d/slx-runmode/scripts/runmode.sh b/modules.d/slx-runmode/scripts/runmode.sh deleted file mode 100644 index a563486f..00000000 --- a/modules.d/slx-runmode/scripts/runmode.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -# -# SLX_SYSTEMD_TARGET overrides the default.target to boot -# the stage4 into - required for the so-called 'run mode'. - -. /etc/openslx - -if [ -n "$SLX_SYSTEMD_TARGET" ]; then - SLX_SYSTEMD_TARGET="${SLX_SYSTEMD_TARGET%.target}.target" - ln -sf "$SLX_SYSTEMD_TARGET" "$NEWROOT/etc/systemd/system/default.target" -fi - -true diff --git a/modules.d/slx-runmode/services/s3-enable-runmode.service b/modules.d/slx-runmode/services/s3-enable-runmode.service new file mode 100644 index 00000000..00f0dcd7 --- /dev/null +++ b/modules.d/slx-runmode/services/s3-enable-runmode.service @@ -0,0 +1,11 @@ +[Unit] +Description=Set up proper default target +After=s3-fetch-config.service +Before=initrd-switch-root.target +DefaultDependencies=no +IgnoreOnIsolate=true + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/bin/s3-enable-runmode.sh diff --git a/modules.d/slx-splash/scripts/restore-cursor.sh b/modules.d/slx-splash/scripts/restore-cursor.sh old mode 100644 new mode 100755 diff --git a/modules.d/slx-splash/scripts/slx-splash-exam.sh b/modules.d/slx-splash/scripts/slx-splash-exam.sh old mode 100644 new mode 100755 diff --git a/modules.d/slx-splash/scripts/slx-splash-icon-config.sh b/modules.d/slx-splash/scripts/slx-splash-icon-config.sh old mode 100644 new mode 100755 diff --git a/modules.d/slx-splash/scripts/slx-splash-icon-hdd.sh b/modules.d/slx-splash/scripts/slx-splash-icon-hdd.sh old mode 100644 new mode 100755 diff --git a/modules.d/slx-splash/scripts/slx-splash-icon-puzzle.sh b/modules.d/slx-splash/scripts/slx-splash-icon-puzzle.sh old mode 100644 new mode 100755 diff --git a/modules.d/slx-splash/scripts/slx-splash-icon-rootfs.sh b/modules.d/slx-splash/scripts/slx-splash-icon-rootfs.sh old mode 100644 new mode 100755 diff --git a/modules.d/slx-splash/scripts/slx-splash-init.sh b/modules.d/slx-splash/scripts/slx-splash-init.sh old mode 100644 new mode 100755 diff --git a/modules.d/slx-ssl/hooks/s3-ssl-sat.sh b/modules.d/slx-ssl/hooks/s3-ssl-sat.sh new file mode 100755 index 00000000..d6fec336 --- /dev/null +++ b/modules.d/slx-ssl/hooks/s3-ssl-sat.sh @@ -0,0 +1,34 @@ +#!/bin/ash + +# $FUTURE_ROOT - root dir of final stage 4 (config.tgz extracted to here) +# $CONFIG - current path to /opt/openslx/config (changes to this end up in stage 4) + +# dracut compat +FUTURE_ROOT="$NEWROOT" +# the slx configuration file is already copied to the newroot in an early hook, so: +CONFIG="${NEWROOT}/opt/openslx/config" +# systemd context, so need to explicitely source the config for SLX_REMOTE_SSL +. "$CONFIG" +# end dracut compat + +# Set up SSL communication with server +if [ -d "${FUTURE_ROOT}/opt/openslx/ssl" ] && [ -n "$(ls -A "${FUTURE_ROOT}/opt/openslx/ssl")" ]; then + # Copy certs and symlinks to CA path + cp -a "${FUTURE_ROOT}"/opt/openslx/ssl/* "${FUTURE_ROOT}/etc/ssl/certs/" + # Append to combined ca bundle + find "${FUTURE_ROOT}/opt/openslx/ssl" -type f -exec cat {} \; \ + >> "${FUTURE_ROOT}/etc/ssl/certs/ca-certificates.crt" + # Move certs (without symlinks), so they will be re-included if we run update-ca-certificates later + mkdir -p "${FUTURE_ROOT}/usr/local/share/ca-certificates" + find "${FUTURE_ROOT}/opt/openslx/ssl" -type f \ + -exec mv {} "${FUTURE_ROOT}/usr/local/share/ca-certificates/" \; + # Delete symlinks + find "${FUTURE_ROOT}/opt/openslx/ssl" -type l -delete + # Add entry to /etc/hosts for boot server (satellite server) + # Change all the URLs in config to use SSL + if [ -n "$SLX_REMOTE_SSL" ] && ! grep -qF 'satellite.bwlehrpool' "/etc/hosts"; then + echo "$SLX_REMOTE_SSL satellite.bwlehrpool" >> "/etc/hosts" + sed -i "s,http://${SLX_REMOTE_SSL}/,https://satellite.bwlehrpool/," "${CONFIG}" + fi +fi +true diff --git a/modules.d/slx-ssl/hooks/setup-ssl-sat.sh b/modules.d/slx-ssl/hooks/setup-ssl-sat.sh deleted file mode 100644 index d6fec336..00000000 --- a/modules.d/slx-ssl/hooks/setup-ssl-sat.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/ash - -# $FUTURE_ROOT - root dir of final stage 4 (config.tgz extracted to here) -# $CONFIG - current path to /opt/openslx/config (changes to this end up in stage 4) - -# dracut compat -FUTURE_ROOT="$NEWROOT" -# the slx configuration file is already copied to the newroot in an early hook, so: -CONFIG="${NEWROOT}/opt/openslx/config" -# systemd context, so need to explicitely source the config for SLX_REMOTE_SSL -. "$CONFIG" -# end dracut compat - -# Set up SSL communication with server -if [ -d "${FUTURE_ROOT}/opt/openslx/ssl" ] && [ -n "$(ls -A "${FUTURE_ROOT}/opt/openslx/ssl")" ]; then - # Copy certs and symlinks to CA path - cp -a "${FUTURE_ROOT}"/opt/openslx/ssl/* "${FUTURE_ROOT}/etc/ssl/certs/" - # Append to combined ca bundle - find "${FUTURE_ROOT}/opt/openslx/ssl" -type f -exec cat {} \; \ - >> "${FUTURE_ROOT}/etc/ssl/certs/ca-certificates.crt" - # Move certs (without symlinks), so they will be re-included if we run update-ca-certificates later - mkdir -p "${FUTURE_ROOT}/usr/local/share/ca-certificates" - find "${FUTURE_ROOT}/opt/openslx/ssl" -type f \ - -exec mv {} "${FUTURE_ROOT}/usr/local/share/ca-certificates/" \; - # Delete symlinks - find "${FUTURE_ROOT}/opt/openslx/ssl" -type l -delete - # Add entry to /etc/hosts for boot server (satellite server) - # Change all the URLs in config to use SSL - if [ -n "$SLX_REMOTE_SSL" ] && ! grep -qF 'satellite.bwlehrpool' "/etc/hosts"; then - echo "$SLX_REMOTE_SSL satellite.bwlehrpool" >> "/etc/hosts" - sed -i "s,http://${SLX_REMOTE_SSL}/,https://satellite.bwlehrpool/," "${CONFIG}" - fi -fi -true diff --git a/modules.d/slx-ssl/module-setup.sh b/modules.d/slx-ssl/module-setup.sh index b3fd803b..5b756e32 100755 --- a/modules.d/slx-ssl/module-setup.sh +++ b/modules.d/slx-ssl/module-setup.sh @@ -9,6 +9,13 @@ depends() { } install() { # config.tgz unpacking happens in pre-pivot/90 - # BEFORE "copy network files" - inst_hook pre-pivot 98 "$moddir/hooks/setup-ssl-sat.sh" + # TODO: BEFORE "copy network files", as we modify /etc/hosts + inst "$moddir/hooks/s3-ssl-sat.sh" \ + /usr/local/bin/s3-ssl-sat.sh + inst_simple "${moddir}/services/s3-ssl-sat.service" \ + "${systemdsystemunitdir}/s3-ssl-sat.service" + mkdir --parents \ + "${initdir}/${systemdsystemunitdir}/dracut-mount.service.requires" + ln_r "${systemdsystemunitdir}/s3-ssl-sat.service" \ + "${systemdsystemunitdir}/dracut-mount.service.requires/s3-ssl-sat.service" } diff --git a/modules.d/slx-ssl/services/s3-ssl-sat.service b/modules.d/slx-ssl/services/s3-ssl-sat.service new file mode 100644 index 00000000..95645334 --- /dev/null +++ b/modules.d/slx-ssl/services/s3-ssl-sat.service @@ -0,0 +1,12 @@ +[Unit] +Description=Enable SSL communication with Satellite Server +After=s3-unpack-config-tgz.service +Requires=s3-unpack-config-tgz.service +Before=initrd-switch-root.target +DefaultDependencies=no +IgnoreOnIsolate=true + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/bin/s3-ssl-sat.sh diff --git a/modules.d/slx-tools/module-setup.sh b/modules.d/slx-tools/module-setup.sh old mode 100644 new mode 100755 diff --git a/modules.d/slx-uuid/hooks/copy-system-uuid-to-newroot.sh b/modules.d/slx-uuid/hooks/copy-system-uuid-to-newroot.sh new file mode 100755 index 00000000..e451ce26 --- /dev/null +++ b/modules.d/slx-uuid/hooks/copy-system-uuid-to-newroot.sh @@ -0,0 +1,6 @@ +#!/bin/ash + +if [ -s "/run/system-uuid" ]; then + cp "/run/system-uuid" "$NEWROOT/etc/system-uuid" +fi +true diff --git a/modules.d/slx-uuid/hooks/s3-get-system-uuid.sh b/modules.d/slx-uuid/hooks/s3-get-system-uuid.sh new file mode 100755 index 00000000..e65a3394 --- /dev/null +++ b/modules.d/slx-uuid/hooks/s3-get-system-uuid.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +# +# Slighty changed version of: +# http://git.openslx.org/openslx-ng/mltk.git/plain/core/modules/system-uuid/data/bin/get-uuid + +. /lib/dracut-lib.sh + +get_system_uuid() { + if [ -e /run/openslx/network.conf ]; then + . /run/openslx/network.conf + else + echo "Don't have /run/openslx/network.conf" + fi + + if [ -z "$SLX_PXE_MAC" ]; then + warn "Getting MAC from /run/openslx/network.conf failed, using 'ip a'..." + for iface in "${SLX_BRIDGE:-br0}" boot0 eth0; do + BOOTIF=01-$(ip a | grep -A 1 ": ${iface}" | grep -o 'ether ..:..:..:..:..:..' | cut -d' ' -f2 | sed s/:/-/g) + [ "${#BOOTIF}" -eq "20" ] && break + done + if [ "${#BOOTIF}" -ne "20" ]; then + warn "Getting MAC from 'ip a' failed, using bogus value..." + BOOTIF="99-88-77-66-55-44-33" + fi + else + BOOTIF="01-$(tr ':' '-' <<< $SLX_PXE_MAC)" + fi + + local UUID=$(dmidecode -q -s system-uuid | grep -v '^#' | head -n 1 | tr 'a-z' 'A-Z') + if [ "${#UUID}" -ne "36" ]; then + warn "Determined UUID (${UUID}) has not expected length of 36, falling back to MAC..." + # Maybe use /proc/sys/kernel/random/uuid ? + UUID= + else + # Got UUID, check blacklist + local DIR="/etc/bad-uuid.d" + local TMPLIST=$(mktemp) + local BADLIST=$(mktemp) + for file in "$DIR"/*; do + [ -f "$file" ] || continue + # 11111111-2222-3333-4444-555555555555 + < "$file" tr 'a-z' 'A-Z' | grep -Eo '[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}' + done | tee "$TMPLIST" > "$BADLIST" + # Also add flipped version of bad uuids. Found some of them through googling and discovered that sometimes + # users report them in a different order. UUIDs use different endianness for the first three blocks than + # the remaining two, but some tools seem to ignore that fact. + < "$BADLIST" sed -r 's/^(..)(..)(..)(..)\-(..)(..)\-(..)(..)\-([0-9A-F]{4}\-[0-9A-F]{12})$/\4\3\2\1-\6\5-\8\7-\9/' >> "$TMPLIST" + # Finally make unique + sort -u "$TMPLIST" > "$BADLIST" + if grep -Fxq "$UUID" "$BADLIST"; then + warn "WARNING: UUID is blacklisted as potentially not being unique, using MAC fallback" + UUID= + fi + rm -f -- "$TMPLIST" "$BADLIST" + fi + + if [ -z "$UUID" ]; then + UUID=$( echo "$BOOTIF" | sed -r 's/[^0-9A-Fa-f]//g' ) + [ "${#UUID}" -eq 14 ] && UUID="${UUID:2}" + if [ "${#UUID}" -eq 12 ]; then + UUID="baad1d00-9491-4716-b98b-$UUID" + else + UUID="baad1d00-9491-4716-b98b-000000000000" + fi + fi + UUID=$( echo "$UUID" | tr 'a-z' 'A-Z' ) + readonly UUID + echo "$UUID" > "/run/system-uuid" +} + +get_system_uuid diff --git a/modules.d/slx-uuid/module-setup.sh b/modules.d/slx-uuid/module-setup.sh index 1e550136..1cac712d 100755 --- a/modules.d/slx-uuid/module-setup.sh +++ b/modules.d/slx-uuid/module-setup.sh @@ -13,17 +13,24 @@ depends() { } install() { mkdir -p "$initdir/etc/bad-uuid.d" - # check if we have an uuid blacklists installed by openslx/mltk on this system - if [ "$(ls -A /opt/openslx/bad-uuid.d)" ]; then - for blacklist in "/opt/openslx/bad-uuid.d"/* ]; do - inst_simple "$blacklist" "/etc/bad-uuid.d/${blacklist##*/}" - done - else - # use this module's default blacklist - inst_simple "$moddir/bad-uuid-defaults.conf" "/etc/bad-uuid.d/bad-uuid-defaults.conf" - fi + # check if we have any uuid blacklists installed by openslx/mltk on this system + for blacklist in /opt/openslx/bad-uuid.d/*; do + [ -s "$blacklist" ] || continue + inst_simple "$blacklist" "/etc/bad-uuid.d/${blacklist##*/}" + done + # add this module's default blacklist + inst_simple "$moddir/bad-uuid-defaults.conf" "/etc/bad-uuid.d/bad-uuid-defaults.conf" inst_multiple dmidecode - inst_hook pre-udev 05 "$moddir/scripts/get-system-uuid.sh" - inst_hook pre-pivot 10 "$moddir/scripts/copy-system-uuid-to-newroot.sh" + inst_hook pre-pivot 10 "$moddir/hooks/copy-system-uuid-to-newroot.sh" + + _name="s3-get-system-uuid" + inst "$moddir/hooks/${_name}.sh" \ + "/usr/local/bin/${_name}.sh" + inst_simple "${moddir}/services/${_name}.service" \ + "${systemdsystemunitdir}/${_name}.service" + mkdir --parents \ + "${initdir}/${systemdsystemunitdir}/dracut-pre-udev.service.requires" + ln_r "${systemdsystemunitdir}/${_name}.service" \ + "${systemdsystemunitdir}/dracut-pre-udev.service.requires/${_name}.service" } diff --git a/modules.d/slx-uuid/scripts/copy-system-uuid-to-newroot.sh b/modules.d/slx-uuid/scripts/copy-system-uuid-to-newroot.sh deleted file mode 100644 index 553109fb..00000000 --- a/modules.d/slx-uuid/scripts/copy-system-uuid-to-newroot.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -if [ -s "/run/system-uuid" ]; then - cp "/run/system-uuid" "$NEWROOT/etc/system-uuid" -fi -true diff --git a/modules.d/slx-uuid/scripts/get-system-uuid.sh b/modules.d/slx-uuid/scripts/get-system-uuid.sh deleted file mode 100644 index fd6bb125..00000000 --- a/modules.d/slx-uuid/scripts/get-system-uuid.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env bash -# -# Slighty changed version of: -# http://git.openslx.org/openslx-ng/mltk.git/plain/core/modules/system-uuid/data/bin/get-uuid - -. /lib/dracut-lib.sh - -get_system_uuid() { - if [ -e /run/openslx/network.conf ]; then - . /run/openslx/network.conf - fi - - if [ -z "$SLX_PXE_MAC" ]; then - eval $(grep -Eo BOOTIF=\\S+ /proc/cmdline) - if [ "${#BOOTIF}" -ne "20" ]; then - warn "Getting MAC from /proc/cmdline failed, using 'ip a'..." - BOOTIF=01-$(ip a | grep -A 1 ": ${SLX_BRIDGE:-br0}" | grep -o 'ether ..:..:..:..:..:..' | cut -d' ' -f2 | sed s/:/-/g) - fi - if [ "${#BOOTIF}" -ne "20" ]; then - warn "Getting MAC from 'ip a' failed, using a default value..." - BOOTIF="99-88-77-66-55-44-33" - fi - else - BOOTIF="01-$(tr ':' '-' <<< $SLX_PXE_MAC)" - fi - - local UUID=$(dmidecode -q -s system-uuid | grep -v '^#' | head -n 1 | tr 'a-z' 'A-Z') - if [ "${#UUID}" -ne "36" ]; then - warn "Determined UUID (${UUID}) has not expected length of 36, falling back to MAC..." - # Maybe use /proc/sys/kernel/random/uuid ? - UUID= - else - # Got UUID, check blacklist - local DIR="/etc/bad-uuid.d" - local TMPLIST=$(mktemp) - local BADLIST=$(mktemp) - for file in "$DIR"/*; do - [ -f "$file" ] || continue - # 11111111-2222-3333-4444-555555555555 - < "$file" tr 'a-z' 'A-Z' | grep -Eo '[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}' - done | tee "$TMPLIST" > "$BADLIST" - # Also add flipped version of bad uuids. Found some of them through googling and discovered that sometimes - # users report them in a different order. UUIDs use different endianness for the first three blocks than - # the remaining two, but some tools seem to ignore that fact. - < "$BADLIST" sed -r 's/^(..)(..)(..)(..)\-(..)(..)\-(..)(..)\-([0-9A-F]{4}\-[0-9A-F]{12})$/\4\3\2\1-\6\5-\8\7-\9/' >> "$TMPLIST" - # Finally make unique - sort -u "$TMPLIST" > "$BADLIST" - if grep -Fxq "$UUID" "$BADLIST"; then - warn "WARNING: UUID is blacklisted as potentially not being unique, using MAC fallback" - UUID= - fi - rm -f -- "$TMPLIST" "$BADLIST" - fi - - if [ -z "$UUID" ]; then - UUID=$( echo "$BOOTIF" | sed -r 's/[^0-9A-Fa-f]//g' ) - [ "${#UUID}" -eq 14 ] && UUID="${UUID:2}" - if [ "${#UUID}" -eq 12 ]; then - UUID="baad1d00-9491-4716-b98b-$UUID" - else - UUID="baad1d00-9491-4716-b98b-000000000000" - fi - fi - UUID=$( echo "$UUID" | tr 'a-z' 'A-Z' ) - readonly UUID - echo "$UUID" > "/run/system-uuid" -} - -get_system_uuid diff --git a/modules.d/slx-uuid/services/s3-get-system-uuid.service b/modules.d/slx-uuid/services/s3-get-system-uuid.service new file mode 100644 index 00000000..b726cd2b --- /dev/null +++ b/modules.d/slx-uuid/services/s3-get-system-uuid.service @@ -0,0 +1,13 @@ +[Unit] +Description=Get system UUID +Requires=dracut-cmdline.service +After=dracut-cmdline.service +Wants=s3-parse-network-kcl.service +After=s3-parse-network-kcl.service +DefaultDependencies=no +IgnoreOnIsolate=true + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/bin/s3-get-system-uuid.sh diff --git a/modules.d/systemd-networkd-ext/hooks/configure-dhcp-for-newroot.sh b/modules.d/systemd-networkd-ext/hooks/configure-dhcp-for-newroot.sh old mode 100644 new mode 100755 diff --git a/modules.d/systemd-networkd-ext/hooks/copy-networkd-files-to-newroot.sh b/modules.d/systemd-networkd-ext/hooks/copy-networkd-files-to-newroot.sh old mode 100644 new mode 100755 diff --git a/modules.d/systemd-networkd-ext/hooks/parse-kcl-for-networkd.sh b/modules.d/systemd-networkd-ext/hooks/parse-kcl-for-networkd.sh old mode 100644 new mode 100755 -- cgit v1.2.3-55-g7522