#!/bin/bash

PROGRAM=$(basename "$0")

usage() {
  cat <<USAGE
Usage:
  $PROGRAM [<option>...]

Options:
  --help              Display this help text.
  --type <virt-type>  Check for execution within the specified VM or container type.

Supported virtualization types:
  container   - Checks for any of the supported container types.
  vm          - Checks for any of the supported VM types.
  <virt-type> - See the list of supported virtualization technologies in
                'man systemd-detect-virt'.

Exit status:
  0  The system is running within the selected virtualization type.
  1  The system is not running within the selected virtualization type.
  2  Wrong usage or unknown error.
USAGE
}

error() {
  echo "$PROGRAM: Error: $*" >&2
  exit 2
}

CMDLINE_OPTS=help,type:
type="__unset__"  # workaround to not have to assume container or vm by default,
                  # nor fail in fallback detection as unsupported type


if ! _opt_temp=$(getopt --name "$0" -o +h --long $CMDLINE_OPTS -- "$@"); then
  error "Try '$0 --help' for more information."
fi
eval set -- "$_opt_temp"

while :; do
  case "$1" in
  --help)
    usage
    exit 0
    ;;
  --type)
    shift
    type="$1"
    ;;
  --)
    shift
    break
    ;;
  *)
    error "Internal getopt error! $1"
    ;;
  esac
  shift
done

fallback_detect_vm() {
  local id="none"

  if [ -e /sys/class/dmi/id/sys_vendor ] ; then
    case "$(cat /sys/class/dmi/id/sys_vendor)" in
      innotek*|VirtualBox)
        # VirtualBox is reported as "oracle" by systemd-detect-virt, try to be consistent
        id=oracle
        ;;
      Xen)
        id=xen
        ;;
      QEMU)
        id=qemu
        ;;
    esac
  fi

  case "$type" in
    oracle|qemu|vm|xen|__unset__)
      ;;
    *)
      error "systemd-detect-virt not found, and VM type '$type' is not supported (yet) in fallback mode."
      ;;
  esac

  echo "$id"
}

fallback_detect_container() {
  local id="none"

  if [ -d /proc/vz/vzaquota ] && ! [ -f /proc/vz/version ] ; then
    id=openvz
  fi

  if [ -r /proc/1/environ ] ; then
    local container
    container=$(tr '\000' '\n' < /proc/1/environ | sed -ne 's/^container=//p')
    if [ "${container}" = "podman" ] ; then
      id=podman
    elif [ -n "${container:-}" ] ; then
      id=lxc
    fi
  fi

  # XXX: We leave this check here so that when we remove the workaround due
  # to the bug in systemd, we do not lose functionality in the fallback.
  if [ -e /.dockerinit ] || [ -e /.dockerenv ] ; then
    id=docker
  fi

  case "$type" in
    container|docker|lxc|openvz|podman|__unset__)
      ;;
    *)
      error "systemd-detect-virt not found, and container type '$type' is not supported (yet) in fallback mode."
      ;;
  esac

  echo "$id"
}

if [ -e /.dockerinit ] || [ -e /.dockerenv ]; then
  # XXX: systemd does not properly detect docker environments:
  # <https://github.com/systemd/systemd/issues/15393>.
  id=docker
elif command -v systemd-detect-virt >/dev/null; then
  case "${type}" in
    "container")
      id="$(systemd-detect-virt --container)"
      ;;
    "vm")
      id="$(systemd-detect-virt --vm)"
      ;;
    *)
      id="$(systemd-detect-virt)"
      ;;
  esac
else
  # In case systemd is not present on the system.
  case "${type}" in
    oracle|qemu|vm|xen)
      id="$(fallback_detect_vm)"
      ;;
    container|docker|lxc|openvz|podman)
      id="$(fallback_detect_container)"
      ;;
    *) # detect whether we are running in any virtualization env
      id="$(fallback_detect_container)"
      if [[ -z "${id}" ]] || [[ "${id:-}" == "none" ]] ; then
        id="$(fallback_detect_vm)"
      fi
      ;;
  esac
fi

case "${type}" in
  "${id}")
    exit 0
    ;;
  container|vm|*)
    if [ "${id}" = "none" ]; then
      exit 1
    fi
    exit 0
    ;;
esac
