#!/bin/bash -ex
# NOTE: This is a hack and will be replaced by some kind of productized code.

ARCH="$(uname -m)"

# Stop the firecracker process if it is already running
API_SOCKET="/root/fc-{{ app_name }}.socket"
rm -f $API_SOCKET

# Run firecracker
pkill -f "${API_SOCKET}" || true
firecracker --api-sock "${API_SOCKET}" &

# set up the kernel boot args and env vars
MASK_LONG="255.255.255.252"
MASK_SHORT="/30"

# Based on how many taps we have, we need to set up the IP addresses
# for the gateway and the tap interface. Pick the first available IP
# address in the range 192.168.0.0/16.
for FC_ID in $(seq 0 255); do
  FC_IP="$(printf '192.168.%s.%s' $(((4 * FC_ID + 1) / 256)) $(((4 * FC_ID + 1) % 256)))"
  TAP_IP="$(printf '192.168.%s.%s' $(((4 * FC_ID + 2) / 256)) $(((4 * FC_ID + 2) % 256)))"
  if ! ip addr show | grep -q $TAP_IP; then
    break
  fi
done

# NOTE: toggle eth0:on to eth0:off to disable networking
KERNEL_BOOT_ARGS="console=ttyS0 rw noapic reboot=k panic=1 pci=off nomodules random.trust_cpu=on ip=${FC_IP}::${TAP_IP}:${MASK_LONG}::eth0:on"

# set up a tap network interface for the Firecracker VM to use
TAP_DEV="fc-$FC_ID-tap0"
ip link del "$TAP_DEV" 2> /dev/null || true
ip tuntap add "$TAP_DEV" mode tap
ip addr add "${TAP_IP}${MASK_SHORT}" dev "$TAP_DEV"
ip link set dev "$TAP_DEV" up

# set up a bridge network interface for the host to use
# NOTE: diable this if not using networking
BRIDGE="fc-$FC_ID-br0"
ip link add name $BRIDGE type bridge
ip link set $TAP_DEV master $BRIDGE
ip link set dev $BRIDGE up
BRIDGE_IP=$(ip addr show dev $BRIDGE | grep -w inet | awk '{print $2}' | awk -F/ '{print $1}')


# Enable packet forwarding for internet access
DEVICE_NAME=$(ip route | grep default | awk '{print $5}')
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -A FORWARD -i $TAP_DEV -o $DEVICE_NAME -j ACCEPT
iptables -A FORWARD -i $DEVICE_NAME -o $TAP_DEV -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -t nat -A POSTROUTING -s $TAP_IP/32 -o $DEVICE_NAME -j MASQUERADE

FC_MAC="$(cat /sys/class/net/$TAP_DEV/address)"

#
# Build and run image to generate rootfs
#
docker build -t {{ app_name }} /root/{{ app_name }}_image
OUTDIR=" /root/{{ app_name }}_outdir"
mkdir -p $OUTDIR
docker run --rm --privileged --env TAP_IP=$TAP_IP --env MASK_SHORT="$MASK_SHORT" --env TAP_DEV=$TAP_DEV --env BRIDGE_IP=$BRIDGE_IP -v $OUTDIR:/opt/code {{ app_name }}
ROOTFSDIR="$(readlink -f {{ app_name }}_fs)"
mv $OUTDIR/alpine-minirootfs-aarch64.ext4 $ROOTFSDIR
ROOTFS_PATH_ON_HOST="/root/{{ app_name }}_fs"

# make a configuration file
cat <<EOF > fc-{{ app_name }}-config.json
{
  "boot-source": {
    "kernel_image_path": "vmlinux.bin",
    "boot_args": "$KERNEL_BOOT_ARGS"
  },
  "drives": [
    {
      "drive_id": "rootfs",
      "path_on_host": "$ROOTFS_PATH_ON_HOST",
      "is_root_device": true,
      "is_read_only": false
    }
  ],
  "network-interfaces": [
      {
          "iface_id": "eth0",
          "guest_mac": "$FC_MAC",
          "host_dev_name": "$TAP_DEV"
      }
  ],
  "machine-config": {
    "vcpu_count": {{ vcpu_count }},
    "mem_size_mib": {{ mem_size_mib }}
  }
}
EOF

# start firecracker
firecracker --no-api --config-file fc-{{ app_name }}-config.json
