#!/bin/sh

SCRIPT=/home/xc_vm
USER=$(whoami)

if [ "$USER" != "root" ]; then
  echo "Please run as root!"
  exit 0
fi

# Launch every XC_VM service. Does NOT block — shared by start (foreground) and
# restart (returns to the shell).
boot() {
  echo 'Starting XC_VM...'
  sudo chown -R xc_vm:xc_vm /sys/class/net
  sudo chown -R xc_vm:xc_vm /home/xc_vm/content/streams
  sudo chown -R xc_vm:xc_vm /home/xc_vm/tmp
  # The node audits' day files (ConnectAudit, SettingsAudit) live under
  # storage/cluster/, which an LB build does not ship: make it xc_vm's before
  # a root process can create it as root's and shut xc_vm's processes out.
  mkdir -p $SCRIPT/storage/cluster
  sudo chown xc_vm:xc_vm $SCRIPT/storage
  sudo chown -R xc_vm:xc_vm $SCRIPT/storage/cluster
  # MAIN: the cluster API answers STARTING until `status` (run by startup)
  # finds its FPM pools answering and writes this marker again (ClusterPool).
  rm -f $SCRIPT/tmp/cluster_ready
  if [ -f $SCRIPT/bin/redis/redis-server ]; then
    # redis.conf holds the password, and dump.rdb every viewer and the
    # panel's cache: xc_vm's only. As xc_vm, not root, which would follow a
    # symlink put in its place.
    sudo -u xc_vm chmod 0700 $SCRIPT/bin/redis 2>/dev/null
    sudo -u xc_vm $SCRIPT/bin/redis/redis-server $SCRIPT/bin/redis/redis.conf >/dev/null 2>/dev/null
  fi
  # The cluster bus (MAIN only: LB builds strip bin/cluster_bus): the cluster
  # API's own Redis, on a unix socket.
  if [ -f $SCRIPT/bin/redis/redis-server ] && [ -f $SCRIPT/bin/cluster_bus/cluster.conf ]; then
    sudo chown -R xc_vm:xc_vm $SCRIPT/bin/cluster_bus 2>/dev/null
    sudo -u xc_vm $SCRIPT/bin/redis/redis-server $SCRIPT/bin/cluster_bus/cluster.conf >/dev/null 2>/dev/null
  fi
  mkdir -p $SCRIPT/bin/xc_fanout/sockets
  sudo chown -R xc_vm:xc_vm $SCRIPT/bin/xc_fanout 2>/dev/null
  # Fanout switched off in the panel (FanoutMode writes this flag): no daemon.
  if [ ! -f $SCRIPT/bin/xc_fanout/disabled ]; then
    sudo -u xc_vm bash $SCRIPT/bin/xc_fanout/run.sh &
  fi
  # Cluster agent: on a node enrolled in the cluster API, and on MAIN with its
  # data-plane client on (main.json; run.sh tells the two apart).
  if [ -x $SCRIPT/bin/xc_agent/xc_agent ] && [ -f $SCRIPT/config/cluster/agent.json -o -f $SCRIPT/config/cluster/main.json ]; then
    sudo chown -R xc_vm:xc_vm $SCRIPT/bin/xc_agent 2>/dev/null
    sudo -u xc_vm bash $SCRIPT/bin/xc_agent/run.sh &
  fi
  # Cluster node replica, CONFIG flow on (bit 32 of flows.json, mode >= 1):
  # rebuild the caches it owns from the copy on disk before the daemons read
  # them (ReplicaApply; cron:cache also does every minute). --from-disk
  # verifies the stored records itself, since the agent may not run yet, and
  # the command boots from the replica (ReplicaStage), so a node rebooted
  # while MAIN is unreachable serves from it. The timeout bounds a stuck disk.
  if [ -f $SCRIPT/config/cluster/flows.json ]; then
    FLOWS=$(sed -n 's/.*"flows":\([0-9]*\).*/\1/p' $SCRIPT/config/cluster/flows.json)
    MODE=$(sed -n 's/.*"mode":\([0-9]*\).*/\1/p' $SCRIPT/config/cluster/flows.json)
    if [ -n "$FLOWS" ] && [ -n "$MODE" ] && [ "$MODE" -ge 1 ] && [ $((FLOWS & 32)) -ne 0 ]; then
      sudo -u xc_vm timeout 15 $SCRIPT/bin/php/bin/php $SCRIPT/console.php cluster:apply --from-disk >/dev/null 2>/dev/null
    fi
  fi
  sudo -u xc_vm $SCRIPT/bin/nginx/sbin/nginx >/dev/null 2>/dev/null
  sudo -u xc_vm $SCRIPT/bin/nginx_rtmp/sbin/nginx_rtmp >/dev/null 2>/dev/null
  sudo -u xc_vm $SCRIPT/bin/daemons.sh
  # startup runs as root by design (root crontab, embedded `status 1`);
  # it delegates the cache builds to xc_vm itself.
  sudo $SCRIPT/bin/php/bin/php $SCRIPT/console.php startup
  sudo chown xc_vm:xc_vm $SCRIPT/config/config.enc 2>/dev/null
  sudo -u xc_vm $SCRIPT/bin/php/bin/php $SCRIPT/console.php signals >/dev/null 2>/dev/null &
  sudo -u xc_vm $SCRIPT/bin/php/bin/php $SCRIPT/console.php watchdog >/dev/null 2>/dev/null &
  sudo -u xc_vm $SCRIPT/bin/php/bin/php $SCRIPT/console.php queue >/dev/null 2>/dev/null &
  # MAIN only: LB builds strip the command (the cron revives it on MAIN).
  if [ -f $SCRIPT/Cli/Commands/CacheHandlerCommand.php ]; then
    sudo -u xc_vm $SCRIPT/bin/php/bin/php $SCRIPT/console.php cache_handler >/dev/null 2>/dev/null &
  fi
  sudo -u xc_vm $SCRIPT/bin/php/bin/php $SCRIPT/console.php fanout_sync >/dev/null 2>/dev/null &
}

# At power-on the clock comes from the hardware clock until NTP first sets it:
# one that runs fast or slow is set back or forward under the running
# services. Wait for that first synchronisation, up to 30 s, then start
# anyway (an unreachable NTP server must not keep a server down).
clock_sync() {
  NTP=$(timedatectl show -p NTP --value 2>/dev/null) || return 0
  if [ "$NTP" != "yes" ]; then
    echo 'WARNING: NTP is off on this server (timedatectl). XC_VM is continuing with the current system clock.'
    return 0
  fi
  for _ in 1 2 3; do
    if [ "$(timedatectl show -p NTPSynchronized --value 2>/dev/null)" = "yes" ]; then
      echo 'Clock synchronized by NTP.'
      return 0
    fi
    sleep 10
  done
  echo 'WARNING: NTP synchronization unavailable after 3 attempts. XC_VM is continuing with the current system clock.'
}

# Foreground entry for systemd (Type=simple): boot, then stay alive so systemd
# keeps tracking the unit. Holds the terminal BY DESIGN — for a manual,
# non-blocking restart use `restart` (or `systemctl restart|reload xc_vm`).
start() {
  pids=$(pgrep -u xc_vm nginx | wc -l)
  if [ $pids != 0 ]; then
    echo 'XC_VM is already running'
    return 1
  fi
  clock_sync
  boot
  echo 'Running in foreground...'
  sleep infinity
}

stop() {
  echo 'Stopping XC_VM...'
  # PHP-FPM masters daemonize out of systemd's cgroup (start-stop-daemon
  # --daemonize + per-user sudo session), so a soft/cgroup stop can leave them —
  # and their OPcache shared memory — alive. Kill EVERYTHING owned by xc_vm by
  # UID, escalating to SIGKILL, so stop/restart truly recycles FPM and OPcache.
  # Always returns 0 so systemd's ExecStop never "fails" (which would skip it).
  pkill -u xc_vm 2>/dev/null || killall -u xc_vm 2>/dev/null || true
  sleep 2
  for _ in 1 2 3; do
    pgrep -u xc_vm >/dev/null 2>&1 || break
    pkill -9 -u xc_vm 2>/dev/null || killall -9 -u xc_vm 2>/dev/null || true
    sleep 1
  done
  return 0
}

restart() {
  # Hard-recycle every xc_vm process EXCEPT ffmpeg (keeps active transcodes/
  # streams running) then boot fresh — reliably flushes OPcache. Returns to the
  # shell; does NOT hold the console (boot, not the foreground start).
  ps -U xc_vm | egrep -v "ffmpeg|PID" | awk '{print $1}' | xargs -r kill -9
  boot
}

reload() {
  pids=$(pgrep -u xc_vm nginx | wc -l)
  if [ $pids = 0 ]; then
    echo 'XC_VM is not running'
    return 1
  fi
  echo 'Reloading XC_VM...'
  sudo -u xc_vm $SCRIPT/bin/nginx/sbin/nginx -s reload
  sudo -u xc_vm $SCRIPT/bin/nginx_rtmp/sbin/nginx_rtmp -s reload
}

case "$1" in
  start)
    start
    ;;
  boot)
    # Everything start does, without holding the terminal: `console.php service
    # start` delegates here, so the panel has one boot sequence, not two.
    boot
    ;;
  stop)
    stop
    ;;
  reload)
    reload
    ;;
  restart)
    restart
    ;;
  *)
    echo "Usage: $0 {start|boot|stop|restart|reload}"
esac

exit 0
