power.sh 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. #!/usr/bin/env bash
  2. set -Eeuo pipefail
  3. # Configure QEMU for graceful shutdown
  4. QEMU_TERM=""
  5. QEMU_PORT=7100
  6. QEMU_TIMEOUT=110
  7. QEMU_PID="/run/shm/qemu.pid"
  8. QEMU_PTY="/run/shm/qemu.pty"
  9. QEMU_LOG="/run/shm/qemu.log"
  10. QEMU_OUT="/run/shm/qemu.out"
  11. QEMU_END="/run/shm/qemu.end"
  12. rm -f /run/shm/qemu.*
  13. touch "$QEMU_LOG"
  14. _trap() {
  15. func="$1" ; shift
  16. for sig ; do
  17. trap "$func $sig" "$sig"
  18. done
  19. }
  20. finish() {
  21. local pid
  22. local reason=$1
  23. if [ -f "$QEMU_PID" ]; then
  24. pid=$(<"$QEMU_PID")
  25. error "Forcefully terminating Windows, reason: $reason..."
  26. { kill -15 "$pid" || true; } 2>/dev/null
  27. while isAlive "$pid"; do
  28. sleep 1
  29. # Workaround for zombie pid
  30. [ ! -f "$QEMU_PID" ] && break
  31. done
  32. fi
  33. pid="/var/run/tpm.pid"
  34. [ -f "$pid" ] && pKill "$(<"$pid")"
  35. closeNetwork
  36. sleep 0.5
  37. echo "❯ Shutdown completed!"
  38. exit "$reason"
  39. }
  40. terminal() {
  41. local dev=""
  42. if [ -f "$QEMU_OUT" ]; then
  43. local msg
  44. msg=$(<"$QEMU_OUT")
  45. if [ -n "$msg" ]; then
  46. if [[ "${msg,,}" != "char"* || "$msg" != *"serial0)" ]]; then
  47. echo "$msg"
  48. fi
  49. dev="${msg#*/dev/p}"
  50. dev="/dev/p${dev%% *}"
  51. fi
  52. fi
  53. if [ ! -c "$dev" ]; then
  54. dev=$(echo 'info chardev' | nc -q 1 -w 1 localhost "$QEMU_PORT" | tr -d '\000')
  55. dev="${dev#*serial0}"
  56. dev="${dev#*pty:}"
  57. dev="${dev%%$'\n'*}"
  58. dev="${dev%%$'\r'*}"
  59. fi
  60. if [ ! -c "$dev" ]; then
  61. error "Device '$dev' not found!"
  62. finish 34 && return 34
  63. fi
  64. QEMU_TERM="$dev"
  65. return 0
  66. }
  67. _graceful_shutdown() {
  68. local code=$?
  69. set +e
  70. if [ -f "$QEMU_END" ]; then
  71. info "Received $1 while already shutting down..."
  72. return
  73. fi
  74. touch "$QEMU_END"
  75. info "Received $1, sending ACPI shutdown signal..."
  76. if [ ! -f "$QEMU_PID" ]; then
  77. error "QEMU PID file does not exist?"
  78. finish "$code" && return "$code"
  79. fi
  80. local pid=""
  81. pid=$(<"$QEMU_PID")
  82. if ! isAlive "$pid"; then
  83. error "QEMU process does not exist?"
  84. finish "$code" && return "$code"
  85. fi
  86. local remove_iso=""
  87. if [ ! -f "$STORAGE/windows.old" ]; then
  88. if [ ! -f "$STORAGE/windows.boot" ] && [ -f "$QEMU_PTY" ]; then
  89. if grep -Fq "Windows Boot Manager" "$QEMU_PTY"; then
  90. [ -f "$STORAGE/$BASE" ] && remove_iso="y"
  91. else
  92. info "Cannot send ACPI signal during Windows setup, aborting..."
  93. finish "$code" && return "$code"
  94. fi
  95. fi
  96. fi
  97. # Send ACPI shutdown signal
  98. echo 'system_powerdown' | nc -q 1 -w 1 localhost "${QEMU_PORT}" > /dev/null
  99. local cnt=0
  100. while [ "$cnt" -lt "$QEMU_TIMEOUT" ]; do
  101. sleep 1
  102. cnt=$((cnt+1))
  103. ! isAlive "$pid" && break
  104. # Workaround for zombie pid
  105. [ ! -f "$QEMU_PID" ] && break
  106. info "Waiting for Windows to shutdown... ($cnt/$QEMU_TIMEOUT)"
  107. # Send ACPI shutdown signal
  108. echo 'system_powerdown' | nc -q 1 -w 1 localhost "${QEMU_PORT}" > /dev/null
  109. done
  110. if [ "$cnt" -ge "$QEMU_TIMEOUT" ]; then
  111. error "Shutdown timeout reached, aborting..."
  112. else
  113. if [ -n "$remove_iso" ]; then
  114. rm -f "$STORAGE/$BASE"
  115. touch "$STORAGE/windows.boot"
  116. fi
  117. fi
  118. finish "$code" && return "$code"
  119. }
  120. SERIAL="pty"
  121. MONITOR="telnet:localhost:$QEMU_PORT,server,nowait,nodelay"
  122. MONITOR="$MONITOR -daemonize -D $QEMU_LOG -pidfile $QEMU_PID"
  123. _trap _graceful_shutdown SIGTERM SIGHUP SIGINT SIGABRT SIGQUIT
  124. return 0