#!/bin/bash

echo Starting /init script ...

PATH=/sbin:/usr/sbin:/bin:/usr/bin
export PATH

# Debian bug 606622.
RUNLEVEL=S
PREVLEVEL=N
export RUNLEVEL PREVLEVEL

# Make sure /tmp /var/tmp are real directories, not symlinks.
if [ ! -d /tmp ] || [ ! -d /var/tmp ]; then
    rm -f /tmp /var/tmp
    mkdir /tmp /var/tmp
    chmod 1777 /tmp /var/tmp
fi

mkdir -p /proc /sys
mount -t proc /proc /proc
mount -t sysfs /sys /sys
# devtmpfs is required since udev 176
mount -t devtmpfs /dev /dev
ln -s /proc/self/fd /dev/fd

# Parse the kernel command line early (must be after /proc is mounted).
cmdline=$(</proc/cmdline)

if [[ $cmdline == *guestfs_verbose=1* ]]; then
    guestfs_verbose=1
    set -x
fi
if [[ $cmdline == *guestfs_network=1* ]]; then
    guestfs_network=1
fi
if [[ $cmdline == *guestfs_rescue=1* ]]; then
    guestfs_rescue=1
fi
if [[ $cmdline == *guestfs_noreboot=1* ]]; then
    guestfs_noreboot=1
fi
if [[ $cmdline == *guestfs_boot_analysis=1* ]]; then
    guestfs_boot_analysis=1
fi

mkdir -p /dev/pts /dev/shm
mount -t devpts /dev/pts /dev/pts
mount -t tmpfs -o mode=1777 shmfs /dev/shm

mkdir -p /sysroot

# taken from initramfs-tools/init --Hilko Bengen
mkdir -p /run
mount -t tmpfs -o "nosuid,size=20%,mode=0755" tmpfs /run
mkdir -p /run/lock
ln -s ../run/lock /var/lock

if [[ $cmdline == *selinux=1* ]]; then
  mount -t selinuxfs none /sys/fs/selinux
fi

# On Fedora 23, util-linux creates /etc/mtab in %post .. stupid
# and e2fsprogs fails if the link doesn't exist .. stupid stupid
if ! test -e /etc/mtab; then
  ln -s /proc/mounts /etc/mtab
fi

# For openssl (RHBZ#2133884).
if test -d /etc/crypto-policies/back-ends &&
        ! test -f /etc/crypto-policies/back-ends/opensslcnf.config &&
        test -f /usr/share/crypto-policies/DEFAULT/opensslcnf.txt ; then
    ln -sf /usr/share/crypto-policies/DEFAULT/opensslcnf.txt /etc/crypto-policies/back-ends/opensslcnf.config
fi

# Static nodes must happen before udev is started.

# Set up kmod static-nodes (RHBZ#1011907).
mkdir -p /run/tmpfiles.d
kmod static-nodes --format=tmpfiles --output=/run/tmpfiles.d/kmod.conf

# Create a machine-id with a random UUID
machine_id=$(dd if=/dev/urandom bs=16 count=1 status=none | od -x -A n)
echo "${machine_id// /}" > /etc/machine-id

# Set up tmpfiles (must run after kmod.conf is created above).
systemd-tmpfiles --prefix=/dev --prefix=/run --prefix=/var/run --create --boot

# Find udevd and run it directly.
for f in /lib/systemd/systemd-udevd /usr/lib/systemd/systemd-udevd \
    /sbin/udevd /lib/udev/udevd \
    /usr/lib/udev/udevd; do
  if [ -x "$f" ]; then UDEVD="$f"; break; fi
done
if [ -z "$UDEVD" ]; then
  echo "error: udev not found!  Things will probably not work ..."
fi

$UDEVD --daemon #--debug
udevadm trigger
udevadm settle --timeout=600

# Disk optimizations.
# Increase the SCSI timeout so we can read remote images.
shopt -s nullglob
for f in /sys/block/sd*/device/timeout; do echo 300 > $f; done
# https://access.redhat.com/site/solutions/5427
for f in /sys/block/{h,s,ub,v}d*/queue/scheduler; do echo noop > $f; done
shopt -u nullglob

# Set up the network.
ip addr add 127.0.0.1/8 brd + dev lo scope host
ip link set dev lo up

if test "$guestfs_network" = 1; then
    iface=$(ls -I all -I default -I lo /proc/sys/net/ipv4/conf)
    # Two workarounds for Ubuntu:
    touch /etc/fstab
    rm -f /etc/dhcp/dhclient-enter-hooks.d/resolved
    if dhclient --version >/dev/null 2>&1; then
        dhclient $iface
    elif dhcpcd $iface; then
        # https://github.com/NetworkConfiguration/dhcpcd/issues/258
        for i in `seq 0 10`; do
            if grep nameserver /etc/resolv.conf; then break; fi
            sleep 1
        done
    fi
fi

# Scan for MDs but don't run arrays unless all expected drives are present
mdadm -As --auto=yes --no-degraded

# Set up a clean LVM environment.
# Empty LVM configuration file means "all defaults".
mkdir -p /tmp/lvm
touch /tmp/lvm/lvm.conf

# If lvm2 supports a "devices file", we need to disable its use
# (RHBZ#1965941).
if command -v lvmdevices || command -v vgimportdevices; then
  {
    printf 'devices {\n'
    printf '\tuse_devicesfile = 0\n'
    printf '}\n'
  } >> /tmp/lvm/lvm.conf
fi

LVM_SYSTEM_DIR=/tmp/lvm
export LVM_SYSTEM_DIR
lvmetad

# Scan for LVM.
modprobe dm_mod ||:
lvm pvscan --cache --activate ay

# Scan for MDs and run all found arrays even they are in degraded state
mdadm -As --auto=yes --run

# Scan for Windows dynamic disks.
ldmtool create all

# These are useful when debugging.
if test "$guestfs_verbose" = 1 && test "$guestfs_boot_analysis" != 1; then
    uname -a
    ls -lR /dev
    cat /proc/mounts
    cat /proc/mdstat
    lvm config
    lvm pvs
    lvm vgs
    lvm lvs
    ip a
    ip r
    cat /etc/resolv.conf
    lsmod
    #hwclock -r
    date
    echo -n "clocksource: "
    cat /sys/devices/system/clocksource/clocksource0/current_clocksource
    #ping -n -v -c 5 8.8.8.8

    echo -n "uptime: "; cat /proc/uptime
fi

# Run the daemon.
cmd="guestfsd"
eval `grep -Eo 'guestfs_channel=[^[:space:]]+' /proc/cmdline`
if test "x$guestfs_channel" != "x"; then
    cmd="$cmd --channel $guestfs_channel"
fi
if test "$guestfs_verbose" = 1; then
    cmd="$cmd --verbose"
fi
if test "$guestfs_network" = 1; then
    cmd="$cmd --network"
fi
if false; then
    # To get a stack trace if the daemon crashes:
    # (1) change this section to 'if true'
    # (2) add 'gdb' to 'appliance/packagelist.in'
    unset LD_PRELOAD
    echo set pagination off  > /tmp/gdb-script
    echo run                >> /tmp/gdb-script
    echo info registers     >> /tmp/gdb-script
    echo 'x/16i $pc'        >> /tmp/gdb-script
    echo t a a bt           >> /tmp/gdb-script
    echo quit               >> /tmp/gdb-script
    cmd="gdb -batch -x /tmp/gdb-script --args $cmd"
fi
if ! test "$guestfs_rescue" = 1; then
    echo $cmd
    $cmd
else
    # Run virt-rescue shell.

    # We need a daemon, even in virt-rescue.
    $cmd &

    # XXX This gives a bit of time for virt-rescue to connect to the
    # daemon and mount any filesystems.
    sleep 2

    # Get name of the serial port, from console= passed by libguestfs.
    # XXX Consider using /proc/consoles
    guestfs_serial=$(grep -Eo 'console=[^[:space:]]+' /proc/cmdline |
                     sed s/console=//)

    # Remove LD_PRELOAD=libSegFault set above.
    unset LD_PRELOAD

    :> $HOME/.bashrc
    grep -Eo 'TERM=[^[:space:]]+' /proc/cmdline >> $HOME/.bashrc
    echo "PS1='><rescue> '" >> $HOME/.bashrc
    echo "export TERM PS1" >> $HOME/.bashrc

    # The shell is opened by default on /dev/console, which (on Linux)
    # is not a controlling terminal, causing job control to fail.  For
    # how we work around this, see:
    # https://busybox.net/FAQ.html#job_control
    run_bash_with_ctty ()
    {
        setsid bash -c \
            "exec bash </dev/$guestfs_serial >/dev/$guestfs_serial 2>&1"
    }

    echo
    echo "------------------------------------------------------------"
    echo
    echo "Welcome to virt-rescue, the libguestfs rescue shell."
    echo
    echo "Note: The contents of / (root) are the rescue appliance."
    if ! test -d "/sysroot/dev"; then
        echo "You have to mount the guest’s partitions under /sysroot"
        echo "before you can examine them."
    else
        echo "Use 'cd /sysroot' or 'chroot /sysroot' to see guest filesystems."
    fi
    echo
    run_bash_with_ctty
    echo
    echo "virt-rescue: Syncing the disk now before exiting ..."
    echo
fi

sync

if ! test "$guestfs_noreboot" = 1; then
  # qemu has the -no-reboot flag, so issuing a reboot here actually
  # causes qemu to exit gracefully.
  reboot -f
fi
