Files
webgui/etc/rc.d/rc.libvirt

310 lines
8.3 KiB
Bash
Executable File

#!/usr/bin/bash
# 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 running 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
if [ -f /boot/config/domain.cfg ]; then
. /boot/config/domain.cfg
fi
SYSTEM=/sys/class/net
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 "}
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|grep -v '^$'
}
waitfor() {
local c=0
while [[ $c -lt $TIMEOUT && $(virsh list --state-${1}|awk "NR>2 && /${2:-^.+$}/"|wc -l) -gt 0 ]]; do
((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
echo "Resuming VM: ${NAMES[$uuid]}"
virsh resume $uuid 1>/dev/null 2>&1
done
# wait until VMs are resumed
waitfor paused
if [[ $HOSTSHUTDOWN == hibernate ]]; then
for uuid in $(vmlist running); do
echo "Suspending VM: ${NAMES[$uuid]}"
virsh dompmsuspend $uuid disk 1>/dev/null 2>&1
done
# wait until VMs are suspended
waitfor running
fi
# graceful shutdown of running VMs
for uuid in $(vmlist running); do
echo "Shutting down VM: ${NAMES[$uuid]}"
virsh shutdown $uuid 1>/dev/null 2>&1
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
echo "Stopping suspended VM: ${NAMES[${uuid[i]}]}"
virsh destroy ${uuid[i]} --graceful 1>/dev/null 2>&1
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
echo "Forced shutting down VM: ${NAMES[$uuid]}"
virsh destroy $uuid 1>/dev/null 2>&1
done
}
check_processor() {
egrep 'vmx' /proc/cpuinfo > /dev/null
if [ "$?" -eq "0" ];then
MODULE="kvm_intel"
fi
check=$?
egrep 'svm' /proc/cpuinfo > /dev/null
if [ "$?" -eq "0" ];then
MODULE="kvm_amd"
fi
check=$(expr $check + $?)
if [ $check -eq "2" ];then
echo "Your system does not support KVM!"
fi
}
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
}
waitstop() {
# wait for daemon to exit
PIDFILE=$1
PNAME=$2
for n in {1..15}; do
sleep 1
if [[ ! -f "$PIDFILE" || $(ps -p $(cat "$PIDFILE") -o comm= 2>/dev/null) != "$PNAME" ]]; then
rm -f "$PIDFILE"
return 0
fi
echo "waiting for $PNAME to die ..."
done
echo "$PNAME will not die!"
return 1
}
version() {
echo $1 | awk -F. '{printf("%03d%03d", $1,$2);}'
}
start_libvirtd() {
if [[ -f $LIBVIRTD_PIDFILE ]]; then
echo "libvirt is already running..."
exit 1
fi
if ! mountpoint /etc/libvirt &> /dev/null ; then
echo "no image mounted at /etc/libvirt"
exit 1
fi
# 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" /etc/libvirt/qemu/*.xml &> /dev/null
# remove <locked/> from xml because libvirt + virlogd + virlockd has an issue with locked
sed -ri "s/<locked\/>//g" /etc/libvirt/qemu/*.xml &> /dev/null
# Remove "<watchdog model='itco' action='reset'/>" if reverting from later release.
. /etc/unraid-version
if [[ $(version $version) -le $(version "6.12") ]]; then
sed -ri "/<watchdog model='itco' action='reset'\/>/d" /etc/libvirt/qemu/*.xml &> /dev/null
fi
# update interface section((s) of VM configuration files
for XML in /etc/libvirt/qemu/*.xml; do
# get all interface sections
ROW=($(grep -nhP '<interface type=' "$XML"|grep -Pom1 '^[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
# change to macvtap
logger -t $(basename $0) "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"
else
# change to bridge
logger -t $(basename $0) "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
done
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
echo "Starting libvirtd..."
mkdir -p $(dirname $LIBVIRTD_PIDFILE)
check_processor
/sbin/modprobe -a $MODULE $MODULES
/usr/sbin/libvirtd -d -l $LIBVIRTD_OPTS
}
stop_libvirtd() {
if [[ ! -f $LIBVIRTD_PIDFILE ]]; then
echo "libvirt is not running..."
exit 2
fi
stop_running_machines
echo "Stopping libvirtd..."
for network in $(/usr/sbin/virsh net-list --uuid | grep -v ^$); do
/usr/sbin/virsh net-destroy $network
done
kill -TERM $(cat $LIBVIRTD_PIDFILE)
waitstop $LIBVIRTD_PIDFILE "libvirtd"
check_processor
/sbin/modprobe -ra $MODULE $MODULES 2>/dev/null
}
start_virtlogd() {
if [[ -f $VIRTLOGD_PIDFILE ]]; then
echo "virtlogd is already running..."
exit 1
fi
echo "Starting virtlogd..."
mkdir -p $(dirname $VIRTLOGD_PIDFILE)
/usr/sbin/virtlogd -d $VIRTLOGD_OPTS
}
stop_virtlogd() {
if [[ ! -f $VIRTLOGD_PIDFILE ]]; then
echo "virtlogd is not running..."
exit 2
fi
echo "Stopping virtlogd..."
kill -TERM $(cat $VIRTLOGD_PIDFILE)
waitstop $VIRTLOGD_PIDFILE "virtlogd"
}
start_virtlockd() {
if [[ -f $VIRTLOCKD_PIDFILE ]]; then
echo "virtlockd is already running..."
exit 1
fi
echo "Starting virtlockd..."
mkdir -p $(dirname $VIRTLOCKD_PIDFILE)
/usr/sbin/virtlockd -d $VIRTLOCKD_OPTS
}
stop_virtlockd() {
if [[ ! -f $VIRTLOCKD_PIDFILE ]]; then
echo "virtlockd is not running..."
exit 2
fi
echo "Stopping virtlockd..."
kill -TERM $(cat $VIRTLOCKD_PIDFILE)
waitstop $VIRTLOCKD_PIDFILE "virtlockd"
}
cleanup() {
find /run/libvirt -type f -exec rm {} \;
}
case $1 in
test)
test
;;
start)
start_virtlockd
start_virtlogd
start_libvirtd
;;
stop)
stop_libvirtd
stop_virtlogd
stop_virtlockd
cleanup
;;
restart)
stop_libvirtd
stop_virtlogd
stop_virtlockd
cleanup
sleep 1
start_virtlockd
start_virtlogd
start_libvirtd
;;
status)
[ -f $LIBVIRTD_PIDFILE ]
;;
*)
echo "Usage: $0 (test|start|stop|restart|status)"
;;
esac