#!/bin/bash
# Stratum module: vm-reactor - VM Reactor: virtual machines (KVM), system containers (LXC)
# and Docker applications in one web interface (package vm-reactor)
#
#   state | status | start | stop | enable | disable | restart |
#   settings | setting-choices <key> | setting <key> <value> |
#   url | list | user ... | docker ... | help
#
# The web interface is the service vm-reactor.service (HTTPS, port 8443 by
# default). Configuration: /etc/vm-reactor, guests: /var/lib/vm-reactor/guests/<id>.
# Guests (machines and containers) keep running when the web interface is
# stopped; "disable" only stops the web interface and its start at boot
# (autostart of guests is done by the web interface at boot).
set -uo pipefail
STRATUM_LIB="${STRATUM_LIB:-/usr/lib/stratum}"
# shellcheck source=/dev/null
. "$STRATUM_LIB/common.sh"

UNIT=vm-reactor.service
CONF=/etc/vm-reactor/vm-reactor.conf
need_root() { [[ $EUID -eq 0 ]] || die "this action must be run as root"; }
conf_get() { [[ -r "$CONF" ]] && sed -n "s/^$1=//p" "$CONF" | tr -d '"' | head -n1; }
port() { local p; p="$(conf_get PORT)"; printf '%s' "${p:-8443}"; }

cmd_state() {
    local run=stopped boot=disabled
    systemctl is-active --quiet "$UNIT" && run=running
    systemctl is-enabled --quiet "$UNIT" 2>/dev/null && boot=enabled
    echo "$run $boot port-$(port)"
}

cmd_status() {
    local s; s="$(cmd_state)"
    echo "VM Reactor - virtual machines, containers and Docker (vm-reactor $(pkg_version vm-reactor))"
    echo
    printf 'Web interface: %s\n' "$(awk '{print $1", at boot "$2}' <<<"$s")"
    printf 'Address:       %s\n' "$(vm-reactor url 2>/dev/null)"
    printf 'Users:         %s\n' "$(grep -cvE '^(#|$)' /etc/vm-reactor/users.conf 2>/dev/null || echo 0)"
    [[ -r /etc/vm-reactor/initial-admin-password ]] && \
        printf 'First sign-in: user admin, password in /etc/vm-reactor/initial-admin-password\n'
    echo
    if [[ $EUID -eq 0 ]]; then vm-reactor list 2>/dev/null; fi
}

cmd_settings() {
    printf 'port\tPort of the web interface\t%s\tnumber\t%s\tPort (1-65535)\n' "$(port)" "$(port)"
    local lf; lf="$(conf_get LISTEN)"
    printf 'listen\tListen on\t%s\tchoice\t%s\n' "${lf:-all addresses}" "${lf:-all}"
}

cmd_setting_choices() {
    case "${1:-}" in
        listen)
            printf 'all\tall addresses of this computer\n'
            ip -4 -o addr show 2>/dev/null | awk '{split($4, a, "/"); print a[1] "\t" $2}' ;;
        *) return 0 ;;
    esac
}

conf_set() {   # conf_set KEY VALUE
    install -d -m 0755 /etc/vm-reactor
    touch "$CONF"
    if grep -q "^$1=" "$CONF"; then sed -i "s|^$1=.*|$1=\"$2\"|" "$CONF"; else echo "$1=\"$2\"" >> "$CONF"; fi
}

cmd_setting() {
    need_root
    local k="${1:-}" v="${2:-}"
    case "$k" in
        port)
            [[ "$v" =~ ^[0-9]+$ ]] && (( v >= 1 && v <= 65535 )) || die "port: 1 to 65535"
            conf_set PORT "$v"; ok "port $v" ;;
        listen)
            [[ "$v" == all ]] && v=""
            [[ -z "$v" ]] || ip -o addr show 2>/dev/null | grep -qF " $v/" || die "$v is not an address of this computer"
            conf_set LISTEN "$v"; ok "listen on ${v:-all addresses}" ;;
        *) die "unknown setting: $k (port, listen)" ;;
    esac
    systemctl is-active --quiet "$UNIT" && systemctl restart "$UNIT"
    ok "address: $(vm-reactor url)"
}

cmd_help() {
    cat <<EOF
Usage: stratum vm-reactor <action>

  status                 the web interface, its address and all guests
  start | stop           start / stop the web interface (guests keep running)
  enable | disable       start the web interface at boot (and now) / stop it and not at boot
  restart                restart the web interface
  setting port <n>       port of the web interface (default 8443)
  setting listen <ip|all>
                         listen on one address of this computer or on all
  url                    the address of the web interface
  list                   all guests (vm-reactor list)
  user ...               users of the web interface (vm-reactor user list|add|passwd|role|2fa-off|del)
  docker ...             Docker and the Docker applications (vm-reactor docker help): setup,
                         repo, settings, set, users, events, prune, create, import,
                         update, rollback, update-mode, limits ...
  help                   this help

Everything else (guests, storage, network, backups) is in the web interface;
on the command line: vm-reactor help.
EOF
}

case "${1:-}" in
    state)    cmd_state ;;
    status)   cmd_status ;;
    start)    need_root; systemctl start "$UNIT" && ok "web interface: $(vm-reactor url)" ;;
    stop)     need_root; systemctl stop "$UNIT" && ok "web interface stopped (guests keep running)" ;;
    restart)  need_root; systemctl restart "$UNIT" && ok "web interface restarted" ;;
    enable)   need_root; systemctl enable --now --quiet "$UNIT" && ok "web interface enabled: $(vm-reactor url)" ;;
    disable)  need_root; systemctl disable --now --quiet "$UNIT" && ok "web interface stopped and disabled (guests keep running)" ;;
    options)  : ;;
    settings) cmd_settings ;;
    setting-choices) shift; cmd_setting_choices "$@" ;;
    setting)  shift; cmd_setting "$@" ;;
    url)      vm-reactor url ;;
    list)     need_root; vm-reactor list ;;
    user)     need_root; shift; vm-reactor user "$@" ;;
    docker)   shift; if [[ "${1:-help}" =~ ^(help|-h|--help|templates|run2compose)$ ]]; then vm-reactor docker "$@"; else need_root; vm-reactor docker "$@"; fi ;;
    help|-h|--help|"") cmd_help ;;
    *) die "unknown action: $1 (see: stratum vm-reactor help)" ;;
esac
