mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-06 20:14:10 +08:00
✨ Sync 2026-07-12 08:33:33
This commit is contained in:
1 parent
01a1d05223
commit
dbdb9f9a83
71 files changed
+18993
-4
No files matched your search
@@ -0,0 +1,46 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=qemu-vms
|
||||
PKG_VERSION:=0.0.1
|
||||
PKG_RELEASE:=1
|
||||
PKG_MAINTAINER:=Konstantine Shevlakov <shevlakov@132lan.ru>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/$(PKG_NAME)
|
||||
SECTION:=utils
|
||||
CATEGORY:=Utilities
|
||||
PKGARCH:=all
|
||||
TITLE:=QEMU Virtual Machines Simple (VMS)
|
||||
DEPENDS:= +qemu-x86_64-softmmu \
|
||||
+qemu-bridge-helper \
|
||||
+minicom \
|
||||
+socat
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/description
|
||||
Scripts to run Virtual Machines using QEMU via uci shell.
|
||||
Support mupltiple run stop and manage machnes via tty.
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/conffiles
|
||||
/etc/config/qemu-vms
|
||||
endef
|
||||
|
||||
|
||||
define Build/Prepare
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
|
||||
define Package/$(PKG_NAME)/install
|
||||
$(CP) ./root/* $(1)/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
config pci-passthrough 'wifi_example'
|
||||
option pci_id '05:00.0'
|
||||
option vendor_id '14c3 7915'
|
||||
|
||||
config usb-passthrough 'example_usb'
|
||||
option vendor_id '0bda'
|
||||
option product_id 'c811'
|
||||
|
||||
config network 'tap_example'
|
||||
option mac '52:54:00:31:6c:4d'
|
||||
option ifname 'tap0'
|
||||
|
||||
config network 'tap_example_bridge'
|
||||
option mac '52:54:00:31:6c:4e'
|
||||
option ifname 'tap1'
|
||||
option bridge 'br-tap'
|
||||
|
||||
config vm 'vm_example'
|
||||
option enabled '1'
|
||||
option image '/mnt/disk/vm/openwrt.img'
|
||||
option memory '256'
|
||||
option smp '1'
|
||||
option cpu 'host'
|
||||
option pci_passthrough 'wifi_ap'
|
||||
option display_type 'serial'
|
||||
list network 'tap_example'
|
||||
|
||||
config vm 'winxp_example'
|
||||
option enabled '1'
|
||||
option memory '2048'
|
||||
option smp '2'
|
||||
option cpu 'qemu64'
|
||||
option display_type 'vnc'
|
||||
option vnc_port '5902'
|
||||
list network 'tap_example_bridge'
|
||||
option cdrom '/mnt/disk/vm/wxp.iso'
|
||||
option image '/mnt/disk/vm/xp.img'
|
||||
option disk_bus 'ide'
|
||||
list custom_arg '-boot order=d'
|
||||
list custom_arg '-machine pc-i440fx'
|
||||
list custom_arg '-global q35-pcihost.x-pci-hole64-fix=false'
|
||||
list custom_arg '-drive if=floppy,file=/mnt/disk/vm/floppy_xp.img,format=raw'
|
||||
list custom_arg '-device VGA,vgamem_mb=64'
|
||||
|
||||
Executable
+249
@@ -0,0 +1,249 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=95
|
||||
STOP=10
|
||||
USE_PROCD=1
|
||||
|
||||
STATUS_DIR="/var/run/qemu-vms"
|
||||
|
||||
is_anonymous_section() {
|
||||
# UCI auto-generated names look like: cfgXXXXXX (cfg + hex)
|
||||
case "$1" in
|
||||
cfg[0-9a-f][0-9a-f][0-9a-f][0-9a-f]*) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
warn_if_anonymous() {
|
||||
local cfg="$1"
|
||||
if is_anonymous_section "$cfg"; then
|
||||
logger -t qemu-vms "WARNING: VM section '$cfg' is anonymous (no name set). " \
|
||||
"Paths (console/qmp socket, pidfile) are derived from this auto-generated " \
|
||||
"UCI id and may change if the config is reordered or the section is recreated. " \
|
||||
"Please set an explicit section name, e.g. config vm 'myvm'."
|
||||
fi
|
||||
}
|
||||
|
||||
build_netargs() {
|
||||
local cfg="$1"
|
||||
idx=0
|
||||
netdev_opts=""
|
||||
device_opts=""
|
||||
config_list_foreach "$cfg" network _add_net
|
||||
}
|
||||
|
||||
_add_net() {
|
||||
local netcfg="$1"
|
||||
local mac ifname bridge
|
||||
|
||||
config_get mac "$netcfg" mac
|
||||
config_get ifname "$netcfg" ifname
|
||||
config_get bridge "$netcfg" bridge
|
||||
|
||||
local id="net${idx}"
|
||||
|
||||
# QEMU's tap "script=" hook only ever calls the script with the tap
|
||||
# ifname as $1 - there is no way to pass a second argument (like the
|
||||
# target bridge) through QEMU itself. So instead of pointing directly
|
||||
# at /etc/qemu-ifup, generate a tiny per-interface wrapper that bakes
|
||||
# in this network section's bridge and calls /etc/qemu-ifup with both
|
||||
# arguments. If "bridge" is empty, the wrapper still runs but passes
|
||||
# an empty $2, and /etc/qemu-ifup simply skips the enslave step.
|
||||
local ifup_wrapper="$STATUS_DIR/qemu-ifup-$netcfg.sh"
|
||||
mkdir -p "$STATUS_DIR"
|
||||
cat > "$ifup_wrapper" <<-EOF
|
||||
#!/bin/sh
|
||||
exec /etc/qemu-ifup "\$1" "$bridge"
|
||||
EOF
|
||||
chmod +x "$ifup_wrapper"
|
||||
|
||||
netdev_opts="$netdev_opts -netdev tap,id=$id,ifname=$ifname,script=$ifup_wrapper,downscript=no"
|
||||
|
||||
local mac_opt=""
|
||||
[ -n "$mac" ] && mac_opt=",mac=$mac"
|
||||
device_opts="$device_opts -device virtio-net-pci${mac_opt},netdev=$id"
|
||||
idx=$((idx + 1))
|
||||
}
|
||||
|
||||
build_usbargs() {
|
||||
local cfg="$1"
|
||||
usb_opts=""
|
||||
usb_needed=0
|
||||
config_list_foreach "$cfg" usb_passthrough _add_usb
|
||||
}
|
||||
|
||||
_add_usb() {
|
||||
local usbcfg="$1"
|
||||
local vendor_id product_id
|
||||
|
||||
config_get vendor_id "$usbcfg" vendor_id
|
||||
config_get product_id "$usbcfg" product_id
|
||||
[ -n "$vendor_id" ] && [ -n "$product_id" ] || return 0
|
||||
|
||||
usb_opts="$usb_opts -device usb-host,vendorid=0x$vendor_id,productid=0x$product_id"
|
||||
usb_needed=1
|
||||
}
|
||||
|
||||
build_customargs() {
|
||||
local cfg="$1"
|
||||
custom_opts=""
|
||||
config_list_foreach "$cfg" custom_arg _add_customarg
|
||||
}
|
||||
|
||||
_add_customarg() {
|
||||
local arg="$1"
|
||||
# Deliberately unquoted below (word-splitting is intentional): a
|
||||
# single list entry like "-device foo,bar=baz" should become two
|
||||
# separate argv elements ("-device" and "foo,bar=baz"), same as if
|
||||
# the admin had typed it directly on a qemu command line. This is an
|
||||
# escape hatch for arbitrary flags we don't have a dedicated UCI
|
||||
# option for - the admin is trusted with their own local config.
|
||||
custom_opts="$custom_opts $arg"
|
||||
}
|
||||
|
||||
build_diskargs() {
|
||||
local cfg="$1"
|
||||
local image="$2"
|
||||
local bus
|
||||
|
||||
config_get bus "$cfg" disk_bus "virtio"
|
||||
|
||||
disk_controller_opts=""
|
||||
disk_opts=""
|
||||
|
||||
case "$bus" in
|
||||
virtio)
|
||||
disk_opts="-drive file=$image,format=raw,if=virtio"
|
||||
;;
|
||||
ide)
|
||||
# Widest guest compatibility (old Windows/DOS/legacy kernels
|
||||
# without virtio drivers), at some cost to I/O throughput.
|
||||
disk_opts="-drive file=$image,format=raw,if=ide"
|
||||
;;
|
||||
scsi)
|
||||
disk_controller_opts="-device virtio-scsi-pci,id=scsi0"
|
||||
disk_opts="-drive file=$image,format=raw,if=none,id=drive0 -device scsi-hd,drive=drive0,bus=scsi0.0"
|
||||
;;
|
||||
sata|ahci)
|
||||
disk_controller_opts="-device ahci,id=ahci0"
|
||||
disk_opts="-drive file=$image,format=raw,if=none,id=drive0 -device ide-hd,drive=drive0,bus=ahci0.0"
|
||||
;;
|
||||
*)
|
||||
logger -t qemu-vms "WARNING: VM '$cfg' has unknown disk_bus '$bus', falling back to virtio"
|
||||
disk_opts="-drive file=$image,format=raw,if=virtio"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
|
||||
start_vm() {
|
||||
local cfg="$1"
|
||||
local enabled image memory smp cpu pci_passthrough
|
||||
|
||||
config_get_bool enabled "$cfg" enabled 0
|
||||
[ "$enabled" = "1" ] || return 0
|
||||
|
||||
local existing_pid
|
||||
if [ -n "$existing_pid" ] && kill -0 "$existing_pid" 2>/dev/null && \
|
||||
grep -aq "qemu-system-x86_64" "/proc/$existing_pid/cmdline" 2>/dev/null; then
|
||||
logger -t qemu-vms "start_vm: '$cfg' already running (pid $existing_pid), skipping"
|
||||
return 0
|
||||
fi
|
||||
|
||||
warn_if_anonymous "$cfg"
|
||||
|
||||
config_get image "$cfg" image
|
||||
config_get cdrom "$cfg" cdrom
|
||||
config_get memory "$cfg" memory 128
|
||||
config_get smp "$cfg" smp 1
|
||||
config_get cpu "$cfg" cpu "host"
|
||||
config_get pci_passthrough "$cfg" pci_passthrough
|
||||
config_get display_type "$cfg" display_type "serial"
|
||||
config_get vnc_port "$cfg" vnc_port
|
||||
|
||||
local console_sock="/var/run/qemu-$cfg.sock"
|
||||
local console_log="/var/log/qemu-$cfg-console.log"
|
||||
local qmp_sock="/var/run/qemu-$cfg.qmp"
|
||||
|
||||
build_netargs "$cfg"
|
||||
build_usbargs "$cfg"
|
||||
build_customargs "$cfg"
|
||||
build_diskargs "$cfg" "$image"
|
||||
|
||||
local pci_id="" vendor_id="" pci_opts=""
|
||||
if [ -n "$pci_passthrough" ]; then
|
||||
config_get pci_id "$pci_passthrough" pci_id
|
||||
config_get vendor_id "$pci_passthrough" vendor_id
|
||||
if [ -n "$vendor_id" ] && [ -n "$pci_id" ]; then
|
||||
[ -e "/sys/bus/pci/devices/0000:$pci_id/driver" ] || \
|
||||
echo "$vendor_id" > /sys/bus/pci/drivers/vfio-pci/new_id 2>/dev/null
|
||||
pci_opts="-device vfio-pci,host=$pci_id"
|
||||
fi
|
||||
fi
|
||||
|
||||
local cdrom_opts=""
|
||||
[ -n "$cdrom" ] && cdrom_opts="-cdrom $cdrom"
|
||||
|
||||
local usb_controller_opts=""
|
||||
[ "$usb_needed" = "1" ] && usb_controller_opts="-device qemu-xhci,id=usb-bus0"
|
||||
|
||||
local vnc_opts=""
|
||||
if [ "$display_type" = "vnc" ] && [ -n "$vnc_port" ]; then
|
||||
local vnc_display=$((vnc_port - 5900))
|
||||
local vnc_ws_port=$((vnc_port + 1000))
|
||||
if [ "$vnc_display" -ge 0 ] 2>/dev/null; then
|
||||
vnc_opts="-vnc :${vnc_display},websocket=${vnc_ws_port}"
|
||||
else
|
||||
logger -t qemu-vms "WARNING: VM '$cfg' has vnc_port=$vnc_port, which is below 5900. Skipping VNC."
|
||||
fi
|
||||
elif [ "$display_type" = "vnc" ]; then
|
||||
logger -t qemu-vms "WARNING: VM '$cfg' has display_type=vnc but no vnc_port set. Skipping VNC."
|
||||
fi
|
||||
|
||||
local display_opts="-display none -nographic"
|
||||
[ -n "$vnc_opts" ] && display_opts=""
|
||||
|
||||
#rm -f "$console_sock" "$qmp_sock"
|
||||
mkdir -p "$STATUS_DIR"
|
||||
mkdir -p "$(dirname "$console_log")"
|
||||
|
||||
procd_open_instance "qemu-$cfg"
|
||||
procd_set_param command /usr/bin/qemu-system-x86_64 \
|
||||
-enable-kvm -cpu "$cpu" -smp "$smp" -m "${memory}M" \
|
||||
$disk_controller_opts $disk_opts \
|
||||
$device_opts $netdev_opts $pci_opts $cdrom_opts $usb_controller_opts $usb_opts $vnc_opts $custom_opts \
|
||||
-device virtio-rng-pci \
|
||||
-machine q35 \
|
||||
-chardev socket,id=con0,path="$console_sock",server=on,wait=off,logfile="$console_log",logappend=on \
|
||||
-serial chardev:con0 \
|
||||
-chardev socket,id=qmp0,path="$qmp_sock",server=on,wait=off \
|
||||
-qmp chardev:qmp0 \
|
||||
-monitor none $display_opts
|
||||
|
||||
procd_set_param respawn 3600 5 5
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
procd_set_param pidfile "$STATUS_DIR/$cfg.pid"
|
||||
procd_close_instance
|
||||
|
||||
# NOTE: no persistent socat/console bridge here on purpose.
|
||||
# QEMU discards console output cleanly when nobody is connected to
|
||||
# console_sock (server=on,wait=off), while logfile= still captures
|
||||
# everything continuously (boot log never stalls). Interactive
|
||||
# access is attached on-demand via /usr/bin/vm-console, which only
|
||||
# connects for the duration of the minicom session.
|
||||
}
|
||||
|
||||
start_service() {
|
||||
config_load qemu-vms
|
||||
if [ -n "$1" ]; then
|
||||
start_vm "$1"
|
||||
else
|
||||
config_foreach start_vm vm
|
||||
fi
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger "qemu-vms"
|
||||
}
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
# /etc/qemu-ifup
|
||||
#
|
||||
# Invoked by QEMU itself (via -netdev tap,...,script=/etc/qemu-ifup) right
|
||||
# after it creates the tap interface. $1 is the tap ifname QEMU created;
|
||||
# $2 (bridge) is passed through via the netdev's "downscript"-adjacent
|
||||
# vhost/helper convention used by build_netargs in /etc/init.d/qemu-vms,
|
||||
# which appends the bridge name as a second positional argument.
|
||||
#
|
||||
# We only enslave the interface into a bridge if one was actually
|
||||
# configured on the network section - a tap interface with no "bridge"
|
||||
# option stays standalone (e.g. for point-to-point or manually managed
|
||||
# setups).
|
||||
|
||||
ifname="$1"
|
||||
bridge="$2"
|
||||
|
||||
ip link set "$ifname" up
|
||||
|
||||
if [ -n "$bridge" ]; then
|
||||
ip link set "$ifname" master "$bridge"
|
||||
fi
|
||||
Executable
+61
@@ -0,0 +1,61 @@
|
||||
#!/bin/sh
|
||||
# vm-console <vm-name> [log|attach]
|
||||
#
|
||||
# attach (default): connects to the VM's QEMU console socket on demand,
|
||||
# bridges it to a temporary PTY via socat, launches minicom, and tears
|
||||
# the bridge down again on exit. QEMU only sees a client connected for
|
||||
# the duration of this session, so boot never stalls waiting for a
|
||||
# reader, and disconnecting minicom never backs up into QEMU.
|
||||
#
|
||||
# log: just tails the always-on boot/console logfile (safe to run at
|
||||
# any time, does not touch the live console socket at all).
|
||||
|
||||
VM="$1"
|
||||
MODE="${2:-attach}"
|
||||
|
||||
if [ -z "$VM" ]; then
|
||||
echo "Usage: vm-console <vm-name> [log|attach]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CONSOLE_SOCK="/var/run/qemu-$VM.sock"
|
||||
CONSOLE_LOG="/var/log/qemu-$VM-console.log"
|
||||
TMP_PTY="/var/run/qemu-$VM.console-pty.$$"
|
||||
|
||||
case "$MODE" in
|
||||
log)
|
||||
[ -f "$CONSOLE_LOG" ] || { echo "No logfile yet: $CONSOLE_LOG" >&2; exit 1; }
|
||||
exec tail -n 200 -f "$CONSOLE_LOG"
|
||||
;;
|
||||
attach)
|
||||
[ -S "$CONSOLE_SOCK" ] || { echo "VM '$VM' is not running (no socket at $CONSOLE_SOCK)" >&2; exit 1; }
|
||||
|
||||
rm -f "$TMP_PTY"
|
||||
socat UNIX-CONNECT:"$CONSOLE_SOCK" PTY,link="$TMP_PTY",raw,echo=0 &
|
||||
SOCAT_PID=$!
|
||||
|
||||
# give socat a moment to create the pty symlink
|
||||
for i in $(seq 1 20); do
|
||||
[ -e "$TMP_PTY" ] && break
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
if [ ! -e "$TMP_PTY" ]; then
|
||||
echo "Failed to create console bridge for '$VM'" >&2
|
||||
kill "$SOCAT_PID" 2>/dev/null
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cleanup() {
|
||||
kill "$SOCAT_PID" 2>/dev/null
|
||||
rm -f "$TMP_PTY"
|
||||
}
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
minicom -D "$TMP_PTY"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown mode: $MODE (expected 'log' or 'attach')" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in new issue
Block a user