#!/bin/bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh

# Originally this was known as 'ignition-generator' found in ignition-dracut.
# With Ignition v 2.5.0, ignition-dracut was merged into Ignition and the CoreOS
# specific bits were deposited here.

set +euo pipefail
. /usr/lib/dracut-lib.sh
set -euo pipefail

# dracut isn't nice with `set -e`
dracut_func() {
    set +euo pipefail
    "$@"; local rc=$?
    set -euo pipefail
    return $rc
}

# Generators don't have logging right now
# https://github.com/systemd/systemd/issues/15638
exec 1>/dev/kmsg; exec 2>&1

UNIT_DIR="${1:-/tmp}"
EARLY_DIR="${2:-/tmp}"

IFS=" " read -r -a cmdline <<< "$(</proc/cmdline)"
cmdline_arg() {
    local name="$1" value=""
    for arg in "${cmdline[@]}"; do
        if [[ "${arg%%=*}" == "${name}" ]]; then
            value="${arg#*=}"
        fi
    done
    echo "${value}"
}

cmdline_bool() {
    local value
    value=$(cmdline_arg "$@")
    case "$value" in
        ""|0|no|off) return 1;;
        *) return 0;;
    esac
}

add_requires() {
    local name="$1"; shift
    local target="$1"; shift
    local requires_dir="${UNIT_DIR}/${target}.requires"
    mkdir -p "${requires_dir}"
    ln -sf "../${name}" "${requires_dir}/${name}"
}

# If this is a live image then exit early.
if is-live-image; then
    exit 0
fi

# Generate a sysroot.mount unit. We can't do this later by dynamically
# doing the mount like we used to because systemd 254+ will drop units that
# require sysroot.mount from the transaction:
# https://github.com/coreos/fedora-coreos-tracker/issues/1527#issuecomment-1656786452
#
# We'll dynamically pick up what mount options to use from the OPTIONS
# environment variable in /run/coreos-rootflags (generated by
# coreos-rootflags.service).
#
# Note that for new machines this is *only* useful on first boot because
# coreos-boot-edit.service will add the root= and rootflags= kargs to the
# kernel command line for subsequent boots. However we haven't migrated
# all old machines so there are still some that could exist that don't
# have root= and rootflags= kargs. For that reason we run this bit of
# code on EVERY diskful boot.
mkdir -p "${UNIT_DIR}"
# We want to generate sysroot.mount on ostree systems, but we don't want
# to run if there's already a root= karg, where the systemd-fstab-generator
# should win.
# We also don't want to generate sysroot.mount if we are booting a kdump kernel
# that aims to upload logs to a remote target, as we may be in a diskless setup
# https://issues.redhat.com/browse/OCPBUGS-27935
# FIXME use dracut-lib https://github.com/coreos/fedora-coreos-config/pull/2890#issuecomment-1985953499
# rather than ConditionPathExists
# Using dracut-lib seems to interfere with iscsi functionnality but this requires deeper investigation
# and more tests : https://github.com/coreos/fedora-coreos-tracker/issues/1689
if test -n "$(cmdline_arg ostree)" && test -z "$(cmdline_arg root)" && ! dracut_func getargbool 0 'kdump_remote_ip'; then
    cat >"${UNIT_DIR}"/sysroot.mount << 'EOF'
[Unit]
Before=initrd-root-fs.target
After=coreos-rootflags.service
[Mount]
EnvironmentFile=/run/coreos-rootflags
What=/dev/disk/by-label/root
Where=/sysroot
Options=${OPTIONS}
EOF
    add_requires sysroot.mount initrd-root-fs.target
    add_requires coreos-rootflags.service initrd-root-fs.target
fi

if ! cmdline_bool 'ignition.firstboot'; then
    exit 0
fi

# coreos-ignition-setup-user.service
# should depend on the boot device node only on diskful boots
mkdir -p "${UNIT_DIR}/coreos-ignition-setup-user.service.d"
cat > "${UNIT_DIR}/coreos-ignition-setup-user.service.d/diskful.conf" <<EOF
[Unit]
Requires=dev-disk-by\x2dlabel-boot.device
After=dev-disk-by\x2dlabel-boot.device
EOF

# If booting from iSCSI, the boot device (where the Ignition config lives)
# is only reachable after networking is up. Note: this is about the Ignition
# config, not the network config — network config is already in the initramfs
# (placed there by coreos-installer) and handled by
# coreos-copy-firstboot-network.service without needing the boot device.
#
# Since `ignition-fetch-offline.service` tries to read the Ignition config
# from the boot device immediately (before networking), it would fail on
# iSCSI. We neuter it and let `ignition-fetch.service` (which runs after
# networking is up) fetch the Ignition config instead.
if test -n "$(cmdline_arg rd.iscsi.firmware)" || test -n "$(cmdline_arg netroot)"; then
    mkdir -p "${EARLY_DIR}/ignition-complete.target.requires"
    ln -sf /dev/null "${EARLY_DIR}/ignition-complete.target.requires/ignition-fetch-offline.service"
    # The boot device is behind the network on iSCSI; neuter the legacy
    # rescue service to avoid a deadlock with its
    # After=dev-disk-by\x2dlabel-boot.device ordering.
    ln -sf /dev/null "${EARLY_DIR}/ignition-complete.target.requires/coreos-rescue-misplaced-network.service"
    mkdir -p /run/ignition
    touch /run/ignition/neednet
fi

# create symlink for udev rule
mkdir -p /run/udev/rules.d/
ln -sf /usr/lib/coreos/80-coreos-boot-disk.rules \
      /run/udev/rules.d/80-coreos-boot-disk.rules

# IBM Secure Execution case
# During firstboot we have to reencrypt '/boot' and '/', to do that an Ignition config
# is injected. 'coreos-boot-disk' is required for this
secure_execution=0
if [[ $(uname -m) == s390x ]] && [[ -e /sys/firmware/uv/prot_virt_guest ]]; then
    secure_execution=$(cat /sys/firmware/uv/prot_virt_guest)
fi
if [[ "${secure_execution}" = "1" ]]; then
    mkdir -p /run/coreos/
    touch /run/coreos/secure-execution
    # Add dropins to disable Ignition logging for all stages
    stages=("fetch-offline" "fetch" "kargs" "disks" "mount" "files")
    for s in "${stages[@]}"; do
        dropin="${UNIT_DIR}/ignition-${s}.service.d"
        mkdir -p "${dropin}"
        cat > "${dropin}/10-secex.conf" <<EOF
[Service]
Environment=IGNITION_ARGS=-log-to-stdout
StandardOutput=null
StandardError=null
EOF
    done

    # This one is done dynamically because it hard Requires a device to appear
    # and if it's always part of the transaction, systemd will want the device
    # to appear regardless of ConditionPathExists.
    add_requires coreos-secex-ignition-prepare.service ignition-diskful.target
fi
