Unverified Commit b276cb2e authored by Kroese's avatar Kroese Committed by GitHub
Browse files

feat: Add optional ACPI shutdown (#1079)

parent bd3d0651
Loading
Loading
Loading
Loading

src/check.sh

0 → 100644
+20 −0
Original line number Diff line number Diff line
#!/usr/bin/env bash
set -Eeuo pipefail

: "${NETWORK:="Y"}"

[ -f "/run/shm/qemu.end" ] && echo "QEMU is shutting down.." && exit 1
[ ! -s "/run/shm/qemu.pid" ] && echo "QEMU is not running yet.." && exit 0
[[ "$NETWORK" == [Nn]* ]] && echo "Networking is disabled.." && exit 0

file="/run/shm/qemu.url"
[ ! -s "$file" ] && echo "The container has not enabled networking yet..." && exit 1

url=$(<"$file")

if ! curl -m 20 -LfSs -o /dev/null "$url"; then
  echo "Failed to reach VM at $url" && exit 1
fi

echo "Healthcheck OK"
exit 0
+1 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ cd /run
. network.sh    # Initialize network
. boot.sh       # Configure boot
. proc.sh       # Initialize processor
. power.sh      # Configure shutdown
. memory.sh     # Check available memory
. balloon.sh    # Initialize ballooning
. config.sh     # Configure arguments
+4 −1
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ set -Eeuo pipefail
: "${NETWORK:="Y"}"
: "${HOST_PORTS:=""}"
: "${USER_PORTS:=""}"
: "${CHECK_PORT:="80"}"
: "${ADAPTER:="virtio-net-pci"}"

: "${VM_NET_IP:=""}"
@@ -944,7 +945,9 @@ fi

NET_OPTS+=" -device $ADAPTER,id=net0,netdev=hostnet0,romfile=,mac=$VM_NET_MAC"
[[ "$MTU" != "0" && "$MTU" != "1500" ]] && NET_OPTS+=",host_mtu=$MTU"
echo "$VM_NET_IP" > /run/shm/qemu.ip

echo "$VM_NET_IP" > "$QEMU_DIR"/qemu.ip
echo "http://$VM_NET_IP:$CHECK_PORT" > "$QEMU_DIR"/qemu.url

html "Initialized network successfully..."
return 0

src/power.sh

0 → 100644
+180 −0
Original line number Diff line number Diff line
#!/usr/bin/env bash
set -Eeuo pipefail

: "${SHUTDOWN:="N"}"        # Graceful ACPI shutdown
: "${QEMU_TIMEOUT:="110"}"  # QEMU Termination timeout

# Configure QEMU for graceful shutdown

QEMU_TERM=""
QEMU_PTY="$QEMU_DIR/qemu.pty"
QEMU_LOG="$QEMU_DIR/qemu.log"
QEMU_OUT="$QEMU_DIR/qemu.out"
QEMU_END="$QEMU_DIR/qemu.end"

_trap() {
  local func="$1"; shift
  local sig
  for sig; do
    trap "$func $sig" "$sig"
  done
}

finish() {

  local pid
  local cnt=0
  local reason=$1
  local pids=( "/var/run/tpm.pid" )

  touch "$QEMU_END"

  if [ -s "$QEMU_PID" ]; then

    pid=$(<"$QEMU_PID")
    echo && error "Forcefully terminating QEMU, reason: $reason..."
    { kill -15 "$pid" || true; } 2>/dev/null

    while isAlive "$pid"; do

      sleep 1
      (( cnt++ ))

      # Workaround for zombie pid
      [ ! -s "$QEMU_PID" ] && break

      if [ "$cnt" -eq 5 ]; then
        echo && error "QEMU did not terminate itself, forcefully killing process..."
        { kill -9 "$pid" || true; } 2>/dev/null
      fi

    done

  fi

  for pid in "${pids[@]}"; do
      if [[ -s "$pid" ]]; then 
          pKill "$(<"$pid")"
      fi
      rm -f "$pid"
  done 

  closeNetwork

  sleep 0.5
  echo "❯ Shutdown completed!"

  exit "$reason"
}

terminal() {

  local dev=""

  if [ -s "$QEMU_OUT" ]; then

    local msg
    msg=$(<"$QEMU_OUT")

    if [ -n "$msg" ]; then

      if [[ "${msg,,}" != "char"* ||  "$msg" != *"serial0)" ]]; then
        echo "$msg"
      fi

      dev="${msg#*/dev/p}"
      dev="/dev/p${dev%% *}"

    fi
  fi

  if [ ! -c "$dev" ]; then
    dev=$(echo 'info chardev' | nc -q 1 -w 1 localhost "$MON_PORT" | tr -d '\000')
    dev="${dev#*serial0}"
    dev="${dev#*pty:}"
    dev="${dev%%$'\n'*}"
    dev="${dev%%$'\r'*}"
  fi

  if [ ! -c "$dev" ]; then
    error "Device '$dev' not found!"
    finish 34 && return 34
  fi

  QEMU_TERM="$dev"
  return 0
}

_graceful_shutdown() {

  local sig="$1"
  local code=0

  case "$sig" in
    SIGTERM) code=143 ;;
    SIGINT)  code=130 ;;
    SIGHUP)  code=129 ;;
    SIGABRT) code=134 ;;
    SIGQUIT) code=131 ;;
  esac

  set +e

  if [ -f "$QEMU_END" ]; then
    info "Received $1 while already shutting down..."
    return
  fi

  touch "$QEMU_END"
  info "Received $1, sending ACPI shutdown signal..."

  if [ ! -s "$QEMU_PID" ]; then
    error "QEMU PID file does not exist?"
    finish "$code" && return "$code"
  fi

  local pid=""
  pid=$(<"$QEMU_PID")

  if ! isAlive "$pid"; then
    error "QEMU process does not exist?"
    finish "$code" && return "$code"
  fi

  # Send ACPI shutdown signal
  echo 'system_powerdown' | nc -q 1 -w 1 localhost "$MON_PORT" > /dev/null

  local cnt=0
  while [ "$cnt" -lt "$QEMU_TIMEOUT" ]; do

    sleep 1
    (( cnt++ ))

    ! isAlive "$pid" && break
    # Workaround for zombie pid
    [ ! -s "$QEMU_PID" ] && break

    info "Waiting for VM to shutdown... ($cnt/$QEMU_TIMEOUT)"

    # Send ACPI shutdown signal
    echo 'system_powerdown' | nc -q 1 -w 1 localhost "$MON_PORT" > /dev/null

  done

  if [ "$cnt" -ge "$QEMU_TIMEOUT" ]; then
    error "Shutdown timeout reached, aborting..."
  fi

  finish "$code" && return "$code"
}

[[ "$SHUTDOWN" != [Yy1]* ]] && return 0

touch "$QEMU_LOG"

SERIAL="pty"
MONITOR="telnet:localhost:$MON_PORT,server,nowait,nodelay -daemonize -D $QEMU_LOG"

_trap _graceful_shutdown SIGTERM SIGHUP SIGINT SIGABRT SIGQUIT

return 0
+6 −1
Original line number Diff line number Diff line
@@ -103,7 +103,6 @@ else
fi

QEMU_PID="$QEMU_DIR/qemu.pid"
rm -f "$QEMU_PID"

# Check folder

@@ -227,4 +226,10 @@ if [[ "$KVM" != [Nn]* ]]; then

fi

# Cleanup dirs
rm -rf "$STORAGE/tmp"

# Cleanup files
rm -f "$QEMU_DIR"/qemu.*

return 0