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

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

command -v getarg >/dev/null || . /usr/lib/dracut-lib.sh

set -e

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

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 ! is-live-image; then
    exit 0
fi

# Create stamp file that everything else should use to detect a live boot
> /run/ostree-live

add_requires sysroot.mount     initrd-root-fs.target
add_requires sysroot-etc.mount initrd-root-fs.target
add_requires sysroot-var.mount initrd-root-fs.target
# make sure we enable network if required for coreos-livepxe-rootfs
# https://github.com/coreos/fedora-coreos-tracker/issues/1423
add_requires coreos-enable-network.service initrd-root-fs.target

mkdir -p "${UNIT_DIR}/ostree-prepare-root.service.d"
cat > "${UNIT_DIR}/ostree-prepare-root.service.d/10-live.conf" <<EOF
# With live PXE there's no ostree= argument on the kernel command line, so
# we need to find the tree path and pass it to ostree-prepare-root.  But
# ostree-prepare-root only knows how to read the path from
# /proc/cmdline, so we need to synthesize the proper karg and bind-mount
# it over /proc/cmdline.
# https://github.com/ostreedev/ostree/issues/1920

[Unit]
# The base unit conditions on the ostree karg, which won't exist until
# ExecStartPre runs
ConditionKernelCommandLine=

[Service]
ExecStartPre=/usr/sbin/ostree-cmdline start
ExecStartPost=/usr/sbin/ostree-cmdline stop
EOF

isoroot=$(getarg coreos.liveiso= ||:)

if [ -z "${isoroot}" ]; then
    # In this case, the rootfs is already unpacked into the initrd, or we need
    # to retrieve it
    cat >"${UNIT_DIR}/sysroot.mount" <<EOF
# Automatically generated by live-generator

[Unit]
DefaultDependencies=false
# Verifies that we have the right sysroot.img, or downloads it if needed
After=coreos-livepxe-rootfs.service
Before=initrd-root-fs.target

[Mount]
What=/sysroot.img
Where=/sysroot
Options=loop
EOF
else
    # And in this case, it's on the ISO
    mkdir -p /run/media/iso
    isosrc=dev/disk/by-label/${isoroot}
    isosrc_escaped=$(systemd-escape -p --suffix=device "${isosrc}")
    initrd_rootdev_target_d="${UNIT_DIR}"/initrd-root-device.target.d
    mkdir -p "${initrd_rootdev_target_d}"
    cat > "${initrd_rootdev_target_d}/50-root-device.conf" <<EOF
[Unit]
After=${isosrc_escaped}
Requires=${isosrc_escaped}
EOF

    cat >"${UNIT_DIR}/run-media-iso.mount" <<EOF
# Automatically generated by live-generator

[Unit]
DefaultDependencies=false
# HACK for https://github.com/coreos/fedora-coreos-config/issues/437
Wants=systemd-udev-settle.service
# Note that bootup(7) implies that initrd-root-device is After=basic.target
# but that appears to not be the case.  We explicitly order after sysinit.target
After=sysinit.target
After=initrd-root-device.target
Before=initrd-root-fs.target
# This will make /run/media/iso get unmounted when no longer needed,
# which will aid in the coreos.liveiso.fromram case.
StopWhenUnneeded=true

[Mount]
What=/${isosrc}
Where=/run/media/iso
Options=ro
Type=iso9660
EOF

    # Determine what to mount in sysroot.mount based on if we were asked to run
    # completely from RAM via coreos.liveiso.fromram karg.
    if getargbool 0 coreos.liveiso.fromram; then
        sysrootrequiresmountsfor=""
        sysrootfsimg=/rootfs.img
        # Add service to copy the rootfs.img from the ISO to memory
        cat >"${UNIT_DIR}/coreos-liveiso-run-media-iso-cp-rootfsimg.service" <<EOF
# Automatically generated by live-generator

[Unit]
RequiresMountsFor=/run/media/iso
Before=sysroot.mount
# DefaultDependencies=true so this unit gets stopped on switchroot to
# allow for /run/media/iso to get unmounted.
DefaultDependencies=true

[Service]
Type=oneshot
ExecStart=cp -v /run/media/iso/images/pxeboot/rootfs.img /rootfs.img
RemainAfterExit=yes
EOF
        add_requires coreos-liveiso-run-media-iso-cp-rootfsimg.service default.target
    else
        sysrootrequiresmountsfor="RequiresMountsFor=/run/media/iso"
        sysrootfsimg=/run/media/iso/images/pxeboot/rootfs.img
    fi

    cat >"${UNIT_DIR}/sysroot.mount" <<EOF
# Automatically generated by live-generator

[Unit]
DefaultDependencies=false
Before=initrd-root-fs.target
After=coreos-liveiso-run-media-iso-cp-rootfsimg.service
${sysrootrequiresmountsfor}

[Mount]
What=${sysrootfsimg}
Where=/sysroot
# Offset of the root.erofs within the rootfs cpio.  Assumes newc format
# and that a file named "root.[squash|ero]fs" is the first member.  This offset
# is checked by coreos-assembler cmd-buildextend-live at build time.
Options=loop,offset=124
EOF
fi

# The systemd default for /run is 20% of RAM, but we want to use basically all of RAM,
# so we make a distinct tmpfs that allows more.  Note the file we create there is
# "thinly provisioned", so we're not actually allocating all of that.
cat >>"${UNIT_DIR}/run-ephemeral_base.mount" <<EOF
[Unit]
DefaultDependencies=false
[Mount]
What=tmpfs
Where=/run/ephemeral_base
Type=tmpfs
Options=size=50%%
EOF

# But it turns out that `tmpfs` currently munches all SELinux labels
# we set before policy is loaded, so we make an XFS filesystem
# loopback mounted on top of the base tmpfs.
# https://github.com/coreos/fedora-coreos-config/pull/499
cat >"${UNIT_DIR}/sysroot-xfs-ephemeral-mkfs.service" <<'EOF'
[Unit]
DefaultDependencies=false
# Let's be sure we have basic devices, but other than that we
# can run really early.
After=systemd-tmpfiles-setup-dev.service
ConditionPathExists=/usr/lib/initrd-release
RequiresMountsFor=/run/ephemeral_base
# Something seems to be causing us to rerun?
ConditionPathExists=!/run/ephemeral_base/loopfs

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/sh -c 'set -euo pipefail; mem=$$(($$(stat -f -c "%%b * %%s / 1024" /run/ephemeral_base))) && /bin/truncate -s $${mem}k /run/ephemeral_base/loopfs'
ExecStart=/sbin/mkfs.xfs /run/ephemeral_base/loopfs
ExecStart=/bin/mkdir /run/ephemeral
EOF
add_requires sysroot-xfs-ephemeral-mkfs.service initrd-root-fs.target

cat >>"${UNIT_DIR}/run-ephemeral.mount" <<EOF
[Unit]
DefaultDependencies=false
Requires=sysroot-xfs-ephemeral-mkfs.service
After=sysroot-xfs-ephemeral-mkfs.service
[Mount]
What=/run/ephemeral_base/loopfs
Where=/run/ephemeral
Type=xfs
Options=loop,discard
EOF

cat >"${UNIT_DIR}/sysroot-xfs-ephemeral-setup.service" <<EOF
[Unit]
DefaultDependencies=false
RequiresMountsFor=/run/ephemeral
ConditionPathExists=/usr/lib/initrd-release
ConditionPathExists=!/run/ephemeral/var
# We want to run after ostree is set up
After=ostree-prepare-root.service
Requires=ostree-prepare-root.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/cp -a /sysroot/etc /run/ephemeral/etc
ExecStart=/bin/mkdir /run/ephemeral/var
EOF

common_etcvar_unit() {
    cat << EOF
# Automatically generated by live-generator
[Unit]
DefaultDependencies=false
# Make sure our tmpfs is available
Requires=sysroot-xfs-ephemeral-setup.service
After=sysroot-xfs-ephemeral-setup.service
# We're part of assembling the root fs
Before=initrd-root-fs.target
EOF
}

common_etcvar_unit > "${UNIT_DIR}/sysroot-etc.mount"
cat >>"${UNIT_DIR}/sysroot-etc.mount" <<EOF
[Mount]
What=/run/ephemeral/etc
Where=/sysroot/etc
Type=none
Options=bind
EOF
add_requires sysroot-etc.mount initrd-root-fs.target

common_etcvar_unit >"${UNIT_DIR}/sysroot-var.mount"
cat >>"${UNIT_DIR}/sysroot-var.mount" <<EOF
[Mount]
What=/run/ephemeral/var
Where=/sysroot/var
Type=none
Options=bind
EOF
add_requires sysroot-var.mount initrd-root-fs.target

cat >>"${UNIT_DIR}/sysroot-relabel.service" <<EOF
[Unit]
DefaultDependencies=false
RequiresMountsFor=/sysroot/etc /sysroot/var
Before=initrd-root-fs.target
[Service]
Type=oneshot
RemainAfterExit=yes
# We don't need the full relabeling spam by default for these
StandardOutput=null
ExecStart=/bin/coreos-relabel /etc
ExecStart=/bin/coreos-relabel /var
EOF
add_requires sysroot-relabel.service initrd-root-fs.target
