Loading src/entry.sh +26 −12 Original line number Diff line number Diff line Loading @@ -29,21 +29,35 @@ cd /run trap - ERR version=$(qemu-system-x86_64 --version | head -n 1 | cut -d '(' -f 1 | awk '{ print $NF }') cmd=(qemu-system-x86_64) version=$("${cmd[@]}" --version | awk 'NR==1 { print $4 }') info "Booting ${APP}${BOOT_DESC} using QEMU v$version..." { qemu-system-x86_64 ${ARGS:+ $ARGS} >"$QEMU_OUT" 2>"$QEMU_LOG"; rc=$?; } || : (( rc != 0 )) && error "$(<"$QEMU_LOG")" && exit 15 if [[ "$SHUTDOWN" != [Yy1]* ]]; then exec "${cmd[@]}" ${ARGS:+ $ARGS} fi terminal ( sleep 30; boot ) & tail -fn +0 "$QEMU_LOG" --pid=$$ 2>/dev/null & cat "$QEMU_TERM" 2> /dev/null | tee "$QEMU_PTY" | \ sed -u -e 's/\x1B\[[=0-9;]*[a-z]//gi' \ -e 's/\x1B\x63//g' -e 's/\x1B\[[=?]7l//g' \ -e '/^$/d' -e 's/\x44\x53\x73//g' \ pipe="$QEMU_DIR/qemu.pipe" rm -f "$pipe" && mkfifo "$pipe" tee "$QEMU_PTY" <"$pipe" | sed -u \ -e 's/\x1B\[[=0-9;]*[a-z]//gi' \ -e 's/\x1B\x63//g' \ -e 's/\x1B\[[=?]7l//g' \ -e '/^$/d' \ -e 's/\x44\x53\x73//g' \ -e 's/failed to load Boot/skipped Boot/g' \ -e 's/0): Not Found/0)/g' & wait $! || : -e 's/0): Not Found/0)/g' & "${cmd[@]}" ${ARGS:+ $ARGS} >"$pipe" & pid=$! ( sleep 30; boot ) & rc=0 wait "$pid" || rc=$? [ -f "$QEMU_END" ] && exit "$rc" sleep 1 & wait $! [ ! -f "$QEMU_END" ] && finish 0 finish "$rc" src/power.sh +88 −121 Original line number Diff line number Diff line #!/usr/bin/env bash set -Eeuo pipefail : "${QEMU_TIMEOUT:="110"}" # QEMU Termination timeout : "${SHUTDOWN:="Y"}" # Graceful ACPI shutdown : "${TIMEOUT:="115"}" # 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 TRAP_PID=$BASHPID for sig; do trap "$func $sig" "$sig" done } app() { echo "$APP" && return 0 } boot() { [ -f "$QEMU_END" ] && return 0 Loading @@ -30,17 +35,14 @@ boot() { grep -Fq "BOOTMGR is missing" "$QEMU_PTY" && fail="y" fi if [ -z "$fail" ]; then info "Windows started successfully, visit http://127.0.0.1:8006/ to view the screen..." info "$(app) started successfully, visit http://127.0.0.1:8006/ to view the screen..." return 0 fi fi fi error "Timeout while waiting for QEMU to boot the machine!" local pid pid=$(<"$QEMU_PID") { kill -15 "$pid" || true; } 2>/dev/null error "Timeout while waiting for QEMU to boot the machine, aborting..." sKill "$QEMU_PID" return 0 } Loading Loading @@ -68,39 +70,29 @@ ready() { finish() { local pid local cnt=0 local i=0 local pid="" local reason=$1 local pids=( "/var/run/tpm.pid" "/var/run/wsdd.pid" "/var/run/samba/nmbd.pid" "/var/run/samba/smbd.pid" ) local pids=( "${SMB_PID:-}" "${NMB_PID:-}" "${DDN_PID:-}" "${TPM_PID:-}" "${WSD_PID:-}" \ "${WEB_PID:-}" "${PASST_PID:-}" "${DNSMASQ_PID:-}" "${BALLOONING_PID:-}" ) touch "$QEMU_END" if [ -s "$QEMU_PID" ]; then pid=$(<"$QEMU_PID") echo && error "Forcefully terminating Windows, 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 if read -r pid <"$QEMU_PID"; then if [ -n "$pid" ] && isAlive "$pid"; then local display="$reason" case "$reason" in 129 ) display="SIGHUP" ;; 130 ) display="SIGINT" ;; 131 ) display="SIGQUIT" ;; 134 ) display="SIGABRT" ;; 143 ) display="SIGTERM" ;; esac error "Forcefully terminating $(app), reason: $display..." { disown "$pid" || :; kill -9 -- "$pid" || :; } 2>/dev/null fi fi done fi if [ ! -f "$STORAGE/windows.boot" ] && [ -f "$BOOT" ]; then Loading @@ -115,131 +107,106 @@ finish() { fi fi for pid in "${pids[@]}"; do if [[ -s "$pid" ]]; then pKill "$(<"$pid")" fi rm -f "$pid" done mKill "${pids[@]}" 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 if ! waitPidFile "$QEMU_PID" 10; then warn "Timed out while waiting for $(app) to exit!" fi QEMU_TERM="$dev" return 0 (( reason != 1 )) && echo && echo "❯ Shutdown completed!" exit "$reason" } _graceful_shutdown() { graceful_shutdown() { local sig="$1" local pid="" local code=0 [[ $BASHPID != "$TRAP_PID" ]] && return case "$sig" in SIGTERM) code=143 ;; SIGINT) code=130 ;; SIGHUP) code=129 ;; SIGABRT) code=134 ;; SIGINT) code=130 ;; SIGQUIT) code=131 ;; SIGABRT) code=134 ;; SIGTERM) code=143 ;; esac if [ -f "$QEMU_END" ]; then info "Received $1 while already shutting down..." echo && info "Received $1 while already shutting down..." return fi set +e touch "$QEMU_END" info "Received $1, sending ACPI shutdown signal..." echo && info "Received $1, sending ACPI shutdown signal..." if [ ! -s "$QEMU_PID" ]; then error "QEMU PID file does not exist?" finish "$code" && return "$code" if [ ! -s "$QEMU_PID" ] || ! read -r pid <"$QEMU_PID"; then warn "QEMU PID file ($QEMU_PID) does not exist?" finish "$code" fi local pid="" pid=$(<"$QEMU_PID") if ! isAlive "$pid"; then error "QEMU process does not exist?" finish "$code" && return "$code" if [ -z "$pid" ] || ! isAlive "$pid"; then warn "QEMU process with PID $pid does not exist?" finish "$code" fi if ! ready; then info "Cannot send ACPI signal during Windows setup, aborting..." finish "$code" && return "$code" info "Cannot send ACPI signal during $(app) setup, aborting..." sKill "$QEMU_PID" if ! waitPidFile "$QEMU_PID" 5; then warn "Timed out while waiting for $(app) to exit!" fi finish "$code" fi # Send ACPI shutdown signal echo 'system_powerdown' | nc -q 1 -w 1 localhost "$MON_PORT" > /dev/null local cnt=0 abort=0 factor=3 offset=3 min max name local cnt=0 while [ "$cnt" -lt "$QEMU_TIMEOUT" ]; do [[ "$TIMEOUT" =~ ^[0-9]+$ ]] || TIMEOUT=115 [ "$TIMEOUT" -ge 15 ] && factor=4 && offset=4 [ "$TIMEOUT" -ge 30 ] && factor=5 && offset=5 min=$((factor + offset + 1)) [ "$TIMEOUT" -lt "$min" ] && TIMEOUT="$min" max=$(( TIMEOUT - offset )) abort=$(( max - factor )) name="$(app)" sleep 1 (( cnt++ )) while [ "$cnt" -le "$max" ]; do sleep 1 & local slp=$! ! isAlive "$pid" && break # Workaround for zombie pid [ ! -s "$QEMU_PID" ] && break info "Waiting for Windows to shutdown... ($cnt/$QEMU_TIMEOUT)" if [ "$cnt" -ne "$abort" ]; then if [ "$cnt" -gt 0 ]; then info "Waiting for $name to shut down... ($cnt/$max)" fi else info "${name^} is still running, sending SIGTERM... ($cnt/$max)" { kill -15 -- "$pid" || :; } 2>/dev/null fi # Send ACPI shutdown signal echo 'system_powerdown' | nc -q 1 -w 1 localhost "$MON_PORT" > /dev/null if [ -S "$QEMU_DIR/monitor.sock" ]; then nc -q 1 -w 1 -U "$QEMU_DIR/monitor.sock" &> /dev/null <<<'system_powerdown' || : fi done wait $slp (( cnt++ )) if [ "$cnt" -ge "$QEMU_TIMEOUT" ]; then error "Shutdown timeout reached, aborting..." fi done finish "$code" && return "$code" finish "$code" } touch "$QEMU_LOG" SERIAL="pty" MONITOR="telnet:localhost:$MON_PORT,server,nowait,nodelay -daemonize -D $QEMU_LOG" [[ "$SHUTDOWN" != [Yy1]* ]] && return 0 [ -n "${QEMU_TIMEOUT:-}" ] && TIMEOUT="$QEMU_TIMEOUT" _trap _graceful_shutdown SIGTERM SIGHUP SIGINT SIGABRT SIGQUIT _trap graceful_shutdown SIGTERM SIGHUP SIGABRT SIGQUIT return 0 src/samba.sh +6 −5 Original line number Diff line number Diff line Loading @@ -7,9 +7,11 @@ set -Eeuo pipefail tmp="/tmp/smb" rm -rf "$tmp" rm -f /var/run/wsdd.pid rm -f /var/run/samba/nmbd.pid rm -f /var/run/samba/smbd.pid DDN_PID="/var/run/wsdd.pid" NMB_PID="/var/run/samba/nmbd.pid" SMB_PID="/var/run/samba/smbd.pid" rm -f "$SMB_PID" "$NMB_PID" "$DDN_PID" [[ "$SAMBA" == [Nn]* ]] && return 0 [[ "$NETWORK" == [Nn]* ]] && return 0 Loading Loading @@ -206,10 +208,9 @@ else # Enable Web Service Discovery on Vista and up [[ "$DEBUG" == [Yy1]* ]] && echo "Starting wsddn daemon..." rm -f /var/log/wsddn.log if ! wsddn -i "${interfaces%%,*}" -H "$hostname" --unixd --log-file=/var/log/wsddn.log --pid-file=/var/run/wsdd.pid; then if ! wsddn -i "${interfaces%%,*}" -H "$hostname" --unixd --log-file=/var/log/wsddn.log --pid-file="$DDN_PID"; then SAMBA_DEBUG="Y" error "Failed to start wsddn daemon!" fi Loading Loading
src/entry.sh +26 −12 Original line number Diff line number Diff line Loading @@ -29,21 +29,35 @@ cd /run trap - ERR version=$(qemu-system-x86_64 --version | head -n 1 | cut -d '(' -f 1 | awk '{ print $NF }') cmd=(qemu-system-x86_64) version=$("${cmd[@]}" --version | awk 'NR==1 { print $4 }') info "Booting ${APP}${BOOT_DESC} using QEMU v$version..." { qemu-system-x86_64 ${ARGS:+ $ARGS} >"$QEMU_OUT" 2>"$QEMU_LOG"; rc=$?; } || : (( rc != 0 )) && error "$(<"$QEMU_LOG")" && exit 15 if [[ "$SHUTDOWN" != [Yy1]* ]]; then exec "${cmd[@]}" ${ARGS:+ $ARGS} fi terminal ( sleep 30; boot ) & tail -fn +0 "$QEMU_LOG" --pid=$$ 2>/dev/null & cat "$QEMU_TERM" 2> /dev/null | tee "$QEMU_PTY" | \ sed -u -e 's/\x1B\[[=0-9;]*[a-z]//gi' \ -e 's/\x1B\x63//g' -e 's/\x1B\[[=?]7l//g' \ -e '/^$/d' -e 's/\x44\x53\x73//g' \ pipe="$QEMU_DIR/qemu.pipe" rm -f "$pipe" && mkfifo "$pipe" tee "$QEMU_PTY" <"$pipe" | sed -u \ -e 's/\x1B\[[=0-9;]*[a-z]//gi' \ -e 's/\x1B\x63//g' \ -e 's/\x1B\[[=?]7l//g' \ -e '/^$/d' \ -e 's/\x44\x53\x73//g' \ -e 's/failed to load Boot/skipped Boot/g' \ -e 's/0): Not Found/0)/g' & wait $! || : -e 's/0): Not Found/0)/g' & "${cmd[@]}" ${ARGS:+ $ARGS} >"$pipe" & pid=$! ( sleep 30; boot ) & rc=0 wait "$pid" || rc=$? [ -f "$QEMU_END" ] && exit "$rc" sleep 1 & wait $! [ ! -f "$QEMU_END" ] && finish 0 finish "$rc"
src/power.sh +88 −121 Original line number Diff line number Diff line #!/usr/bin/env bash set -Eeuo pipefail : "${QEMU_TIMEOUT:="110"}" # QEMU Termination timeout : "${SHUTDOWN:="Y"}" # Graceful ACPI shutdown : "${TIMEOUT:="115"}" # 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 TRAP_PID=$BASHPID for sig; do trap "$func $sig" "$sig" done } app() { echo "$APP" && return 0 } boot() { [ -f "$QEMU_END" ] && return 0 Loading @@ -30,17 +35,14 @@ boot() { grep -Fq "BOOTMGR is missing" "$QEMU_PTY" && fail="y" fi if [ -z "$fail" ]; then info "Windows started successfully, visit http://127.0.0.1:8006/ to view the screen..." info "$(app) started successfully, visit http://127.0.0.1:8006/ to view the screen..." return 0 fi fi fi error "Timeout while waiting for QEMU to boot the machine!" local pid pid=$(<"$QEMU_PID") { kill -15 "$pid" || true; } 2>/dev/null error "Timeout while waiting for QEMU to boot the machine, aborting..." sKill "$QEMU_PID" return 0 } Loading Loading @@ -68,39 +70,29 @@ ready() { finish() { local pid local cnt=0 local i=0 local pid="" local reason=$1 local pids=( "/var/run/tpm.pid" "/var/run/wsdd.pid" "/var/run/samba/nmbd.pid" "/var/run/samba/smbd.pid" ) local pids=( "${SMB_PID:-}" "${NMB_PID:-}" "${DDN_PID:-}" "${TPM_PID:-}" "${WSD_PID:-}" \ "${WEB_PID:-}" "${PASST_PID:-}" "${DNSMASQ_PID:-}" "${BALLOONING_PID:-}" ) touch "$QEMU_END" if [ -s "$QEMU_PID" ]; then pid=$(<"$QEMU_PID") echo && error "Forcefully terminating Windows, 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 if read -r pid <"$QEMU_PID"; then if [ -n "$pid" ] && isAlive "$pid"; then local display="$reason" case "$reason" in 129 ) display="SIGHUP" ;; 130 ) display="SIGINT" ;; 131 ) display="SIGQUIT" ;; 134 ) display="SIGABRT" ;; 143 ) display="SIGTERM" ;; esac error "Forcefully terminating $(app), reason: $display..." { disown "$pid" || :; kill -9 -- "$pid" || :; } 2>/dev/null fi fi done fi if [ ! -f "$STORAGE/windows.boot" ] && [ -f "$BOOT" ]; then Loading @@ -115,131 +107,106 @@ finish() { fi fi for pid in "${pids[@]}"; do if [[ -s "$pid" ]]; then pKill "$(<"$pid")" fi rm -f "$pid" done mKill "${pids[@]}" 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 if ! waitPidFile "$QEMU_PID" 10; then warn "Timed out while waiting for $(app) to exit!" fi QEMU_TERM="$dev" return 0 (( reason != 1 )) && echo && echo "❯ Shutdown completed!" exit "$reason" } _graceful_shutdown() { graceful_shutdown() { local sig="$1" local pid="" local code=0 [[ $BASHPID != "$TRAP_PID" ]] && return case "$sig" in SIGTERM) code=143 ;; SIGINT) code=130 ;; SIGHUP) code=129 ;; SIGABRT) code=134 ;; SIGINT) code=130 ;; SIGQUIT) code=131 ;; SIGABRT) code=134 ;; SIGTERM) code=143 ;; esac if [ -f "$QEMU_END" ]; then info "Received $1 while already shutting down..." echo && info "Received $1 while already shutting down..." return fi set +e touch "$QEMU_END" info "Received $1, sending ACPI shutdown signal..." echo && info "Received $1, sending ACPI shutdown signal..." if [ ! -s "$QEMU_PID" ]; then error "QEMU PID file does not exist?" finish "$code" && return "$code" if [ ! -s "$QEMU_PID" ] || ! read -r pid <"$QEMU_PID"; then warn "QEMU PID file ($QEMU_PID) does not exist?" finish "$code" fi local pid="" pid=$(<"$QEMU_PID") if ! isAlive "$pid"; then error "QEMU process does not exist?" finish "$code" && return "$code" if [ -z "$pid" ] || ! isAlive "$pid"; then warn "QEMU process with PID $pid does not exist?" finish "$code" fi if ! ready; then info "Cannot send ACPI signal during Windows setup, aborting..." finish "$code" && return "$code" info "Cannot send ACPI signal during $(app) setup, aborting..." sKill "$QEMU_PID" if ! waitPidFile "$QEMU_PID" 5; then warn "Timed out while waiting for $(app) to exit!" fi finish "$code" fi # Send ACPI shutdown signal echo 'system_powerdown' | nc -q 1 -w 1 localhost "$MON_PORT" > /dev/null local cnt=0 abort=0 factor=3 offset=3 min max name local cnt=0 while [ "$cnt" -lt "$QEMU_TIMEOUT" ]; do [[ "$TIMEOUT" =~ ^[0-9]+$ ]] || TIMEOUT=115 [ "$TIMEOUT" -ge 15 ] && factor=4 && offset=4 [ "$TIMEOUT" -ge 30 ] && factor=5 && offset=5 min=$((factor + offset + 1)) [ "$TIMEOUT" -lt "$min" ] && TIMEOUT="$min" max=$(( TIMEOUT - offset )) abort=$(( max - factor )) name="$(app)" sleep 1 (( cnt++ )) while [ "$cnt" -le "$max" ]; do sleep 1 & local slp=$! ! isAlive "$pid" && break # Workaround for zombie pid [ ! -s "$QEMU_PID" ] && break info "Waiting for Windows to shutdown... ($cnt/$QEMU_TIMEOUT)" if [ "$cnt" -ne "$abort" ]; then if [ "$cnt" -gt 0 ]; then info "Waiting for $name to shut down... ($cnt/$max)" fi else info "${name^} is still running, sending SIGTERM... ($cnt/$max)" { kill -15 -- "$pid" || :; } 2>/dev/null fi # Send ACPI shutdown signal echo 'system_powerdown' | nc -q 1 -w 1 localhost "$MON_PORT" > /dev/null if [ -S "$QEMU_DIR/monitor.sock" ]; then nc -q 1 -w 1 -U "$QEMU_DIR/monitor.sock" &> /dev/null <<<'system_powerdown' || : fi done wait $slp (( cnt++ )) if [ "$cnt" -ge "$QEMU_TIMEOUT" ]; then error "Shutdown timeout reached, aborting..." fi done finish "$code" && return "$code" finish "$code" } touch "$QEMU_LOG" SERIAL="pty" MONITOR="telnet:localhost:$MON_PORT,server,nowait,nodelay -daemonize -D $QEMU_LOG" [[ "$SHUTDOWN" != [Yy1]* ]] && return 0 [ -n "${QEMU_TIMEOUT:-}" ] && TIMEOUT="$QEMU_TIMEOUT" _trap _graceful_shutdown SIGTERM SIGHUP SIGINT SIGABRT SIGQUIT _trap graceful_shutdown SIGTERM SIGHUP SIGABRT SIGQUIT return 0
src/samba.sh +6 −5 Original line number Diff line number Diff line Loading @@ -7,9 +7,11 @@ set -Eeuo pipefail tmp="/tmp/smb" rm -rf "$tmp" rm -f /var/run/wsdd.pid rm -f /var/run/samba/nmbd.pid rm -f /var/run/samba/smbd.pid DDN_PID="/var/run/wsdd.pid" NMB_PID="/var/run/samba/nmbd.pid" SMB_PID="/var/run/samba/smbd.pid" rm -f "$SMB_PID" "$NMB_PID" "$DDN_PID" [[ "$SAMBA" == [Nn]* ]] && return 0 [[ "$NETWORK" == [Nn]* ]] && return 0 Loading Loading @@ -206,10 +208,9 @@ else # Enable Web Service Discovery on Vista and up [[ "$DEBUG" == [Yy1]* ]] && echo "Starting wsddn daemon..." rm -f /var/log/wsddn.log if ! wsddn -i "${interfaces%%,*}" -H "$hostname" --unixd --log-file=/var/log/wsddn.log --pid-file=/var/run/wsdd.pid; then if ! wsddn -i "${interfaces%%,*}" -H "$hostname" --unixd --log-file=/var/log/wsddn.log --pid-file="$DDN_PID"; then SAMBA_DEBUG="Y" error "Failed to start wsddn daemon!" fi Loading