Files
webgui/etc/rc.d/rc.libvirt
2025-04-07 13:31:02 +01:00

359 lines
9.7 KiB
Bash
Executable File

#!/bin/bash
#
# script: rc.libvirt
#
# Init script for libvirtd on Slackware
# Written by Matteo Bernardini <ponce@slackbuilds.org>
#
# Note that a dnsmasq daemon is started by libvirtd itself to serve
# its virtual network, and possibly can conflict with a dnsmasq
# Already started on the system, see
# http://wiki.libvirt.org/page/Libvirtd_and_dnsmasq
# Note also that the tun, vhost_net and kvm related modules are
# automatically loaded at start and removed at stop: edit the
# script if this behaviour conflicts with anything else running
# on your setup
#
# Read Unraid libvirt configuration file (named "domain.cfg" leftover from Xen days)
#
# Modified stop_running_machines by Bergware 2022-03-11
# Avoid Unraid from hanging when the array is stopped, while VMs are in paused or suspended state
#
# LimeTech - modified for Unraid OS
# Bergware - modified for Unraid OS, October 2023
DAEMON="libvirt daemon"
MODULES=${MODULES:-"vhost_net"}
TIMEOUT=${TIMEOUT:-60}
HOSTSHUTDOWN=${HOSTSHUTDOWN:-"shutdown"}
LIBVIRTD_PIDFILE="/var/run/libvirt/libvirtd.pid"
LIBVIRTD_OPTS=${LIBVIRT_OPTS:-" -f /etc/libvirt/libvirtd.conf -p $LIBVIRTD_PIDFILE "}
VIRTLOGD_PIDFILE="/var/run/libvirt/virtlogd.pid"
VIRTLOGD_OPTS=${VIRTLOGD_OPTS:-" -f /etc/libvirt/virtlogd.conf -p $VIRTLOGD_PIDFILE "}
VIRTLOCKD_PIDFILE="/var/run/libvirt/virtlockd.pid"
VIRTLOCKD_OPTS=${VIRTLOCKD_OPTS:-" -f /etc/libvirt/virtlockd.conf -p $VIRTLOCKD_PIDFILE "}
BOOT_DOMAIN="/boot/config/domain.cfg"
SYSTEM="/sys/class/net"
VIRTLOG="virtlog daemon"
VIRTLOCK="virtlock daemon"
# run & log functions
. /etc/rc.d/rc.runlog
# Read Unraid configuration
[[ -f $BOOT_DOMAIN ]] && . $BOOT_DOMAIN
vmstate(){
virsh list --all | awk -F'[[:space:]][[:space:]]+' 'NR>2 && /^.+$/{print $3}'
}
vmlist(){
local STATE=
for ARG in ${@: 1}; do
[[ $ARG == all ]] && STATE="$STATE --all" || STATE="$STATE --state-$ARG"
done
virsh list --uuid $STATE | tail -n +1 | grep -v '^$'
}
waitfor(){
local C=0
while [[ $C -lt $TIMEOUT && $(virsh list --state-$1 | awk "NR>2 && /${2:-^.+$}/" | wc -l) -gt 0 ]]; do
if [[ $C -eq 0 ]]; then # echo Timeout info just one time and only if virsh returned something
log "Waiting $TIMEOUT seconds for VMs with state: $1"
fi
((C++))
sleep 1
done
}
stop_running_machines(){
# create names array
declare -A NAMES
while IFS='\n' read -r ID; do
[[ -n $ID ]] && NAMES[${ID:0:36}]=${ID:37}
done <<< $(virsh list --all --uuid --name)
# resume paused VMs
for UUID in $(vmlist paused); do
log "Resuming VM: ${NAMES[$UUID]}"
virsh resume $UUID &>/dev/null
done
# wait until VMs are resumed
waitfor paused
if [[ $HOSTSHUTDOWN == hibernate ]]; then
for UUID in $(vmlist running); do
log "Suspending VM: ${NAMES[$UUID]}"
virsh dompmsuspend $UUID disk &>/dev/null
done
# wait until VMs are suspended
waitfor running
fi
# graceful shutdown of running VMs
for UUID in $(vmlist running); do
log "Shutting down VM: ${NAMES[$UUID]}"
virsh shutdown $UUID &>/dev/null
done
# wait until VMs are stopped
waitfor running
# graceful shutdown of suspended VMs
i=0; UUID=($(vmlist all))
while IFS='\n' read -r STATE; do
# check explicitely for suspended VMs
if [[ $STATE == pmsuspended ]]; then
log "Stopping suspended VM: ${NAMES[${UUID[$i]}]}"
virsh destroy ${UUID[$i]} --graceful &>/dev/null
fi
((I++))
done <<< $(vmstate)
# wait until VMs are stopped
waitfor other pmsuspended
# forced shutdown of rogue VMs
for UUID in $(vmlist running paused other); do
log "Forced shutting down VM: ${NAMES[$UUID]}"
virsh destroy $UUID &>/dev/null
done
}
check_processor(){
grep 'vmx' /proc/cpuinfo >/dev/null
if [[ $? -eq 0 ]]; then
MODULE="kvm_intel"
fi
CHECK=$?
grep 'svm' /proc/cpuinfo >/dev/null
if [[ $? -eq 0 ]]; then
MODULE="kvm_amd"
fi
CHECK=$(expr $CHECK + $?)
if [[ $CHECK -eq 2 ]]; then
log "Your system does not support KVM!"
fi
}
libvirtd_test(){
check_processor
if [[ -z $MODULE ]]; then
exit 1
fi
if lsmod | grep -q "^$MODULE"; then
echo $MODULE
exit 0
fi
modprobe $MODULE
if lsmod | grep -q "^$MODULE"; then
modprobe -r $MODULE
echo $MODULE
exit 0
fi
exit 1
}
wait_stop(){
# wait for daemon to exit
PIDFILE=$1
PROCESS=$2
for N in {1..15}; do
sleep 1
if [[ ! -f "$PIDFILE" || $(ps -p $(cat "$PIDFILE") -o comm= 2>/dev/null) != "$PROCESS" ]]; then
rm -f "$PIDFILE"
return 0
fi
echo "waiting for $PROCESS to die ..."
done
log "$PROCESS will not die!"
return 1
}
version(){
echo $1 | awk -F. '{printf("%03d%03d",$1,$2);}'
}
libvirtd_start(){
log "Starting $DAEMON..."
if ! mountpoint /etc/libvirt &>/dev/null; then
log "$DAEMON... No image mounted at /etc/libvirt."
exit 1
elif [[ -f $LIBVIRTD_PIDFILE ]]; then
log "$DAEMON... Already started."
return 1
fi
. /etc/unraid-version
[[ $(version $version) -le $(version "6.12") ]] && LEGACY=1 || LEGACY=
# update interface section((s) of VM configuration files
for XML in /etc/libvirt/qemu/*.xml; do
[[ -f "$XML" ]] || continue
if grep -qm1 "<vendor_id='none'/>" "$XML"; then
# convert libvirt 1.3.1 w/ eric's hyperv vendor id patch to how libvirt does it in libvirt 1.3.3+
sed -ri "s/<vendor id='none'\/>/<vendor_id state='on' value='none'\/>/g" "$XML"
fi
if grep -qm1 "<locked/>" "$XML"; then
# remove <locked/> from xml because libvirt + virlogd + virlockd has an issue with locked
sed -ri "/<locked\/>/d" "$file"
fi
if [[ -n $LEGACY ]] && grep -qm1 "<watchdog model='itco' action='reset'/>" "$XML"; then
# Remove "<watchdog model='itco' action='reset'/>" if reverting from later release.
sed -ri "/<watchdog model='itco' action='reset'\/>/d" "$XML"
fi
# get all interface sections
ROW=($(grep -nhP '<interface type=' "$XML" | grep -Po '^[0-9]+'))
# get all source sections
CAT=($(grep -nhP '<source (bridge|dev)=' "$XML" | awk '{print $1$3}'))
for i in ${!ROW[@]}; do
ROW2=$(echo ${CAT[$i]} | grep -Pom1 '^[0-9]+')
DEV=$(echo ${CAT[$i]} | grep -Pom1 "^.+='\K[^']+")
if [[ ! -e $SYSTEM/$DEV ]]; then
NAME=${DEV//[0-9.]/}
if [[ $NAME == br ]]; then
if grep -qm1 "<interface type='bridge'>" "$XML"; then
# change to macvtap
log "change $DEV to macvtap in $XML"
sed -ri "${ROW[$i]} s/<interface type='bridge'>/<interface type='direct' trustGuestRxFilters='yes'>/; $ROW2 s/<source bridge='$DEV'\/>/<source dev='${DEV/$NAME/vhost}' mode='bridge'\/>/" "$XML"
fi
else
if grep -qm1 "<interface type='direct'" "$XML"; then
# change to bridge
log "change $DEV to bridge in $XML"
sed -ri "${ROW[$i]} s/<interface type='direct'( trustGuestRxFilters='yes')?>/<interface type='bridge'>/; $ROW2 s/<source dev='$DEV' mode='bridge'\/>/<source bridge='${DEV/$NAME/br}'\/>/" "$XML"
fi
fi
fi
done
sed -i 's|<vmtemplate xmlns="unraid|<vmtemplate xmlns="http://unraid|' "$XML"
done
# copy any new conf files we dont currently have
cp -n /etc/libvirt-/*.conf /etc/libvirt &>/dev/null
# ensure tpm-states path exists
mkdir -p /etc/libvirt/qemu/swtpm/tpm-states
# setup snapshot persistance.
mkdir -p /etc/libvirt/qemu/snapshot
mkdir -p /etc/libvirt/qemu/snapshotdb
rm -rf /var/lib/libvirt/qemu/snapshot
ln -sf /etc/libvirt/qemu/snapshot /var/lib/libvirt/qemu/snapshot
# create directory for pid file
mkdir -p $(dirname $LIBVIRTD_PIDFILE)
check_processor
modprobe -a $MODULE $MODULES
echo 0 > /sys/module/kvm/parameters/report_ignored_msrs
libvirtd -d -l $LIBVIRTD_OPTS
log "$DAEMON... Started."
}
libvirtd_stop(){
log "Stopping $DAEMON..."
if [[ ! -f $LIBVIRTD_PIDFILE ]]; then
log "$DAEMON... Already stopped."
return 1
fi
stop_running_machines
for network in $(virsh net-list --uuid | grep -v ^$); do
run virsh net-destroy $network
done
kill -TERM $(cat $LIBVIRTD_PIDFILE)
wait_stop $LIBVIRTD_PIDFILE $DAEMON
check_processor
modprobe -ra $MODULE $MODULES 2>/dev/null
log "$DAEMON... Stopped."
}
virtlogd_start(){
log "Starting $VIRTLOG..."
if [[ -f $VIRTLOGD_PIDFILE ]]; then
log "$VIRTLOG... Already started."
return 1
fi
mkdir -p $(dirname $VIRTLOGD_PIDFILE)
virtlogd -d $VIRTLOGD_OPTS
log "$VIRTLOG... Started."
}
virtlogd_stop(){
log "Stopping $VIRTLOG..."
if [[ ! -f $VIRTLOGD_PIDFILE ]]; then
log "$VIRTLOG... Already stopped."
return 1
fi
kill -TERM $(cat $VIRTLOGD_PIDFILE)
wait_stop $VIRTLOGD_PIDFILE $VIRTLOG
log "$VIRTLOG... Stopped."
}
virtlockd_start(){
log "Starting $VIRTLOCK..."
if [[ -f $VIRTLOCKD_PIDFILE ]]; then
log "$VIRTLOCK... Already started."
return 1
fi
mkdir -p $(dirname $VIRTLOCKD_PIDFILE)
virtlockd -d $VIRTLOCKD_OPTS
log "$VIRTLOCK... Started."
}
virtlockd_stop(){
log "Stopping $VIRTLOCK..."
if [[ ! -f $VIRTLOCKD_PIDFILE ]]; then
log "$VIRTLOCK... Already stopped."
return 1
fi
kill -TERM $(cat $VIRTLOCKD_PIDFILE)
wait_stop $VIRTLOCKD_PIDFILE $VIRTLOCK
log "$VIRTLOCK... Stopped."
}
libvirtd_status(){
if [[ -f $LIBVIRTD_PIDFILE ]]; then
echo "$DAEMON is currently running."
else
echo "$DAEMON is not running."
exit 1
fi
}
libvirtd_cleanup(){
find /run/libvirt -type f -exec rm "{}" \;
sleep 1
}
case "$1" in
'test')
libvirtd_test
;;
'start')
virtlockd_start
virtlogd_start
libvirtd_start
;;
'stop')
libvirtd_stop
virtlogd_stop
virtlockd_stop
libvirtd_cleanup
;;
'restart')
libvirtd_stop
virtlogd_stop
virtlockd_stop
libvirtd_cleanup
virtlockd_start
virtlogd_start
libvirtd_start
;;
'status')
libvirtd_status
;;
'stopvm')
stop_running_machines
;;
*)
echo "Usage: $BASENAME test|start|stop|restart|status"
exit 1
esac
exit 0