✨ Sync 2026-07-29 08:29:27

This commit is contained in:
github-actions[bot] committed 2026-07-29 08:29:27 +08:00
1 parent b17aa6f5de
commit 3cdc6def95
10 files changed
+143 -27

No files matched your search

+1 -1
View File
@@ -4,7 +4,7 @@ LUCI_TITLE:=Web UI for QEMU Virtual Machine Simple (VMS)
LUCI_DEPENDS:=+qemu-vms +ttyd +usbutils +pciutils
PKG_LICENSE:=GPLv3
PKG_VERSION:=0.0.1
PKG_RELEASE:=1
PKG_RELEASE:=2
include $(TOPDIR)/feeds/luci/luci.mk
@@ -297,8 +297,8 @@ return view.extend({
var rows = devices.map(function(dev) {
var section = self.findPciSection(dev.addr);
var owner = self.pciOwner(dev.addr);
var passthroughStatus = section ? _('Passthrough active (section: %s)').format(section) : _('Not passthrough');
if (owner) passthroughStatus += ' ' + _('(attached to VM: %s)').format(owner);
var passthroughStatus = section ? _('Active<br />Section: %s').format(section) : _('Not passthrough');
if (owner) passthroughStatus += ' ' + _('<br />Host: %s').format(owner);
var actionButton;
if (section) {
@@ -325,11 +325,11 @@ return view.extend({
return E('table', { 'class': 'table' }, [
E('tr', { 'class': 'tr table-titles' }, [
E('th', { 'class': 'th' }, _('PCI address')),
E('th', { 'class': 'th' }, _('Address')),
E('th', { 'class': 'th' }, _('Vendor')),
E('th', { 'class': 'th' }, _('Device')),
E('th', { 'class': 'th' }, _('Current driver')),
E('th', { 'class': 'th' }, _('Passthrough status')),
E('th', { 'class': 'th' }, _('Driver')),
E('th', { 'class': 'th', 'style': 'min-width: 140px;' }, _('Status')),
E('th', { 'class': 'th' }, _('Action'))
])
].concat(rows));
@@ -343,8 +343,8 @@ return view.extend({
var section = (vendorId && productId) ? self.findUsbSection(vendorId, productId) : null;
var owners = (vendorId && productId) ? self.usbOwners(vendorId, productId) : [];
var passthroughStatus = section ? _('Passthrough active (section: %s)').format(section) : _('Not passthrough');
if (owners.length) passthroughStatus += ' ' + _('(attached to VM: %s)').format(owners.join(', '));
var passthroughStatus = section ? _('Active<br />Section: %s').format(section) : _('Not passthrough');
if (owners.length) passthroughStatus += ' ' + _('<br />Host: %s').format(owners.join(', '));
var actionButton;
if (section) {
@@ -371,9 +371,9 @@ return view.extend({
return E('table', { 'class': 'table' }, [
E('tr', { 'class': 'tr table-titles' }, [
E('th', { 'class': 'th' }, _('Bus:Dev')),
E('th', { 'class': 'th' }, _('VendorID:ProductID')),
E('th', { 'class': 'th' }, _('VID:PID')),
E('th', { 'class': 'th' }, _('Description')),
E('th', { 'class': 'th' }, _('Passthrough status')),
E('th', { 'class': 'th', 'style': 'min-width: 140px;' }, _('Status')),
E('th', { 'class': 'th' }, _('Action'))
])
].concat(rows));
@@ -190,7 +190,7 @@ return view.extend({
handleLog: function(name, ev) {
return callReadlog(name, 400).then(function(res) {
ui.showModal(_('Console log: %s').format(name), [
ui.showModal(_('Instanse log: %s').format(name), [
E('pre', {
'style': 'max-height: 60vh; overflow: auto; white-space: pre-wrap; font-size: 12px;'
}, res.log || _('(empty)')),
@@ -493,6 +493,9 @@ return view.extend({
E('label', { 'class': 'cbi-value-title' }, _('Attached networks')),
E('div', { 'class': 'cbi-value-field' }, networkChecks)
]),
E('div', { 'class': 'cbi-value' }, [
E('p', { 'class': 'cbi-value-description' }, _('Changes will take effect after restarting the VM.'))
]),
E('div', { 'class': 'right', 'style': 'margin-top: 1em' }, [
E('button', { 'class': 'btn', 'click': ui.hideModal }, _('Cancel')),
' ',
@@ -590,6 +593,13 @@ return view.extend({
else
uci.unset('qemu-vms', name, 'pci_passthrough');
var newUsb = Array.prototype.slice.call(document.querySelectorAll('.edit-usb-check:checked'))
.map(function(el) { return el.value; });
if (newUsb.length)
uci.set('qemu-vms', name, 'usb_passthrough', newUsb);
else
uci.unset('qemu-vms', name, 'usb_passthrough');
//var nets = Array.prototype.slice.call(document.querySelectorAll('.edit-network-check:checked'))
// .map(function(el) { return el.value; });
@@ -145,7 +145,7 @@ console_close() {
readlog() {
local vm="$1"
local lines="${2:-200}"
local log="/var/log/qemu-$vm-console.log"
local log="/var/log/qemu-vms/$vm.log"
json_init
if [ -f "$log" ]; then
+1 -1
View File
@@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=qemu-vms
PKG_VERSION:=0.0.1
PKG_RELEASE:=1
PKG_RELEASE:=2
PKG_MAINTAINER:=Konstantine Shevlakov <shevlakov@132lan.ru>
include $(INCLUDE_DIR)/package.mk
+95 -10
View File
@@ -5,6 +5,23 @@ STOP=10
USE_PROCD=1
STATUS_DIR="/var/run/qemu-vms"
LOG_DIR="/var/log/qemu-vms"
host_log() {
# Per-VM HOST-side lifecycle log: start/stop attempts, config
# warnings (missing MAC, unknown disk_bus, VNC without a port,
# passthrough bind issues), and QEMU's own stderr (KVM unavailable,
# image not found, device init failures). Deliberately separate from
# the guest's own serial console capture
# (/var/log/qemu-$vm-console.log): that one shows what the GUEST OS
# printed; this one shows whether the HOST managed to start/
# configure the VM at all - meaningful for VNC-mode VMs too, which
# have no serial console capture at all.
local cfg="$1"
shift
mkdir -p "$LOG_DIR"
echo "$(date '+%Y-%m-%d %H:%M:%S') $*" >> "$LOG_DIR/$cfg.log"
}
is_anonymous_section() {
# UCI auto-generated names look like: cfgXXXXXX (cfg + hex)
@@ -84,6 +101,27 @@ _add_usb() {
usb_needed=1
}
_unbind_usb_host() {
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
for dev in /sys/bus/usb/devices/*; do
if [ -f "$dev/idVendor" ] && [ -f "$dev/idProduct" ]; then
local v=$(cat "$dev/idVendor") p=$(cat "$dev/idProduct")
if [ "$v" = "$vendor_id" ] && [ "$p" = "$product_id" ]; then
if [ -L "$dev/driver" ]; then
local driver_path=$(readlink -f "$dev/driver")
local driver_name=$(basename "$driver_path")
echo -n "$(basename "$dev")" > "$driver_path/unbind" 2>/dev/null
host_log "$cfg" "USB: unbound $(basename "$dev") from $driver_name"
fi
fi
fi
done
}
build_customargs() {
local cfg="$1"
custom_opts=""
@@ -113,24 +151,30 @@ build_diskargs() {
case "$bus" in
virtio)
disk_opts="-drive file=$image,format=raw,if=virtio"
#disk_opts="-drive file=$image,format=raw,if=virtio"
disk_opts="-drive file=$image,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"
#disk_opts="-drive file=$image,format=$format,if=ide"
disk_opts="-drive file=$image,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"
#disk_opts="-drive file=$image,format=$format,if=none,id=drive0 -device scsi-hd,drive=drive0,bus=scsi0.0"
disk_opts="-drive file=$image,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"
#disk_opts="-drive file=$image,format=$format,if=none,id=drive0 -device ide-hd,drive=drive0,bus=ahci0.0"
disk_opts="-drive file=$image,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"
#disk_opts="-drive file=$image,format=$format,if=virtio"
disk_opts="-drive file=$image,if=virtio"
host_log "$cfg" "WARNING: unknown disk_bus '$bus', falling back to virtio"
;;
esac
}
@@ -145,12 +189,15 @@ start_vm() {
[ "$enabled" = "1" ] || return 0
local existing_pid
existing_pid="$(cat "$STATUS_DIR/$cfg.pid" 2>/dev/null)"
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
host_log "$cfg" "=== start_vm: attempting to start ==="
warn_if_anonymous "$cfg"
config_get image "$cfg" image
@@ -158,6 +205,7 @@ start_vm() {
config_get memory "$cfg" memory 128
config_get smp "$cfg" smp 1
config_get cpu "$cfg" cpu "host"
config_list_foreach "$cfg" usb_passthrough _unbind_usb_host
config_get pci_passthrough "$cfg" pci_passthrough
config_get display_type "$cfg" display_type "serial"
config_get vnc_port "$cfg" vnc_port
@@ -176,12 +224,47 @@ start_vm() {
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" ] || \
if [ -e "/sys/bus/pci/devices/0000:$pci_id/driver" ]; then
local current_driver
current_driver="$(basename "$(readlink -f "/sys/bus/pci/devices/0000:$pci_id/driver" 2>/dev/null)" 2>/dev/null)"
host_log "$cfg" "PCI passthrough: $pci_id already bound to driver '$current_driver'"
else
echo "$vendor_id" > /sys/bus/pci/drivers/vfio-pci/new_id 2>/dev/null
if [ -e "/sys/bus/pci/devices/0000:$pci_id/driver" ]; then
host_log "$cfg" "PCI passthrough: bound $pci_id (vendor/device $vendor_id) to vfio-pci"
else
host_log "$cfg" "WARNING: PCI passthrough: failed to bind $pci_id to vfio-pci - check IOMMU/BIOS settings"
fi
fi
pci_opts="-device vfio-pci,host=$pci_id"
else
host_log "$cfg" "WARNING: pci_passthrough='$pci_passthrough' is set but its pci_id/vendor_id are incomplete - skipping passthrough"
fi
fi
# Test version PCI Pass
#local pci_id="" pci_opts=""
#if [ -n "$pci_passthrough" ]; then
# config_get pci_id "$pci_passthrough" pci_id
# if [ -n "$pci_id" ]; then
# local pci_path="/sys/bus/pci/devices/0000:$pci_id"
# if [ -e "$pci_path/driver" ]; then
# echo -n "0000:$pci_id" > "$pci_path/driver/unbind" 2>/dev/null
# host_log "$cfg" "PCI: unbound $pci_id from $(basename "$(readlink -f "$pci_path/driver")")"
# fi
# echo "vfio-pci" > "$pci_path/driver_override" 2>/dev/null
# echo "0000:$pci_id" > /sys/bus/pci/drivers/vfio-pci/bind 2>/dev/null
# if [ -e "$pci_path/driver" ] && [ "$(basename "$(readlink -f "$pci_path/driver")")" = "vfio-pci" ]; then
# host_log "$cfg" "PCI passthrough: $pci_id successfully bound to vfio-pci"
# else
# host_log "$cfg" "ERROR: PCI passthrough: failed to bind $pci_id to vfio-pci. Check IOMMU groups and BIOS settings."
# fi
# pci_opts="-device vfio-pci,host=$pci_id"
# else
# host_log "$cfg" "WARNING: pci_passthrough section exists but pci_id is empty - skipping"
# fi
#fi
local cdrom_opts=""
[ -n "$cdrom" ] && cdrom_opts="-cdrom $cdrom"
@@ -206,10 +289,12 @@ start_vm() {
#rm -f "$console_sock" "$qmp_sock"
mkdir -p "$STATUS_DIR"
mkdir -p "$LOG_DIR"
mkdir -p "$(dirname "$console_log")"
procd_open_instance "qemu-$cfg"
procd_set_param command /usr/bin/qemu-system-x86_64 \
procd_set_param command /usr/libexec/qemu-vms/run-with-log.sh "$LOG_DIR/$cfg.log" \
/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 \
@@ -244,6 +329,6 @@ start_service() {
fi
}
service_triggers() {
procd_add_reload_trigger "qemu-vms"
}
#service_triggers() {
# procd_add_reload_trigger "qemu-vms"
#}
+21
View File
@@ -0,0 +1,21 @@
#!/bin/sh
# /usr/libexec/qemu-vms/run-with-log.sh
#
# Usage: run-with-log.sh <logfile> <command> [args...]
#
# Appends the wrapped command's stdout/stderr to <logfile>, then execs it.
# Deliberately does NOT build a shell string out of <command>/[args...] -
# they are received and forwarded as genuine argv elements ("$@"), never
# re-parsed as shell syntax. This matters because some of the arguments
# QEMU is started with (e.g. custom_arg from the LuCI "Custom QEMU
# arguments" field) are admin-supplied free text: if this script instead
# did something like `sh -c "exec $* >>logfile"`, shell metacharacters in
# that text (;, `, $(...), |, &&) would be interpreted as shell syntax
# and executed - not just passed to qemu as inert argv text. Using "$@"
# throughout preserves the same "argv, not a string" trust model as a
# direct (unwrapped) exec.
logfile="$1"
shift
exec "$@" >> "$logfile" 2>&1
+1 -1
View File
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=sqm-autorate
PKG_VERSION:=0.1
PKG_RELEASE:=2
PKG_RELEASE:=3
include $(INCLUDE_DIR)/package.mk
@@ -61,7 +61,7 @@ set -o pipefail
export LC_ALL=C
# Set PREFIX
PREFIX=/root/cake-autorate
PREFIX=/usr/share/sqm-autorate
# shellcheck source=lib.sh
. "${PREFIX}/lib.sh"
@@ -1,6 +1,6 @@
#!/bin/bash
cake_instances=(/root/cake-autorate/config.*.sh)
cake_instances=(/usr/share/sqm-autorate/config.*.sh)
cake_instance_pids=()
trap kill_cake_instances INT TERM EXIT
@@ -20,7 +20,7 @@ kill_cake_instances()
for cake_instance in "${cake_instances[@]}"
do
/root/cake-autorate/cake-autorate.sh "${cake_instance}" &
/usr/share/sqm-autorate/cake-autorate.sh "${cake_instance}" &
cake_instance_pids+=(${!})
done
wait