From 53d56be8f92797a2323b6836c1b37f7c43ba358b Mon Sep 17 00:00:00 2001 From: MengMengCode <227010654+MengMengCode@users.noreply.github.com> Date: Wed, 22 Jul 2026 16:57:44 +0800 Subject: [PATCH] fix #33 --- backend/internal/kvm/kvm.go | 22 +++++++++++++++------- backend/internal/kvm/kvm_test.go | 21 +++++++++++++++++++++ install.sh | 12 ++++++++++-- 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/backend/internal/kvm/kvm.go b/backend/internal/kvm/kvm.go index ee77faa..3bbc148 100644 --- a/backend/internal/kvm/kvm.go +++ b/backend/internal/kvm/kvm.go @@ -1724,12 +1724,12 @@ func ensureDefaultNetwork() error { // Ensure libvirtd is running if err := exec.Command("systemctl", "start", "libvirtd").Run(); err != nil { // Non-systemd systems may use a different init, try virsh connect - if exec.Command("virsh", "connect").Run() != nil { + if virshCLocaleCommand("connect").Run() != nil { return fmt.Errorf("libvirtd is not running and could not be started") } } // Ensure default network is defined - if exec.Command("virsh", "net-info", "default").Run() != nil { + if virshCLocaleCommand("net-info", "default").Run() != nil { // Default network may not be defined; try to define it netXML := ` default @@ -1746,7 +1746,7 @@ func ensureDefaultNetwork() error { return fmt.Errorf("failed to write default network XML: %v", err) } defer os.Remove(tmpFile) - if out, err := exec.Command("virsh", "net-define", tmpFile).CombinedOutput(); err != nil { + if out, err := virshCLocaleCommand("net-define", tmpFile).CombinedOutput(); err != nil { return fmt.Errorf("failed to define libvirt default network: %v, output: %s", err, string(out)) } if err := os.MkdirAll(filepath.Dir(libvirtDefaultNetworkMarker), 0755); err == nil { @@ -1754,19 +1754,27 @@ func ensureDefaultNetwork() error { } } // Start and autostart the default network - if out, err := exec.Command("virsh", "net-info", "default").Output(); err == nil { + if out, err := virshCLocaleCommand("net-info", "default").Output(); err == nil { if !libvirtNetworkActive(string(out)) { - if startOut, startErr := exec.Command("virsh", "net-start", "default").CombinedOutput(); startErr != nil { - return fmt.Errorf("failed to start libvirt default network: %v, output: %s", startErr, string(startOut)) + if startOut, startErr := virshCLocaleCommand("net-start", "default").CombinedOutput(); startErr != nil { + if verifyOut, verifyErr := virshCLocaleCommand("net-info", "default").Output(); verifyErr != nil || !libvirtNetworkActive(string(verifyOut)) { + return fmt.Errorf("failed to start libvirt default network: %v, output: %s", startErr, string(startOut)) + } } } } - if out, err := exec.Command("virsh", "net-autostart", "default").CombinedOutput(); err != nil { + if out, err := virshCLocaleCommand("net-autostart", "default").CombinedOutput(); err != nil { return fmt.Errorf("failed to set autostart for libvirt default network: %v, output: %s", err, string(out)) } return nil } +func virshCLocaleCommand(args ...string) *exec.Cmd { + cmd := exec.Command("virsh", args...) + cmd.Env = append(os.Environ(), "LC_ALL=C", "LC_MESSAGES=C", "LANG=C", "LANGUAGE=C") + return cmd +} + func libvirtNetworkActive(info string) bool { for _, line := range strings.Split(info, "\n") { key, value, ok := strings.Cut(line, ":") diff --git a/backend/internal/kvm/kvm_test.go b/backend/internal/kvm/kvm_test.go index 76bf438..6aa230a 100644 --- a/backend/internal/kvm/kvm_test.go +++ b/backend/internal/kvm/kvm_test.go @@ -24,6 +24,27 @@ func TestImagePathUsesAllowlistedImageID(t *testing.T) { } } +func TestLibvirtNetworkActiveParsesCLocaleOutput(t *testing.T) { + tests := []struct { + name string + info string + want bool + }{ + {name: "active", info: "Name: default\nActive: yes\n", want: true}, + {name: "spacing and case", info: " Active : YES \r\n", want: true}, + {name: "inactive", info: "Name: default\nActive: no\n", want: false}, + {name: "missing field", info: "Name: default\nAutostart: yes\n", want: false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if got := libvirtNetworkActive(tc.info); got != tc.want { + t.Fatalf("libvirtNetworkActive(%q) = %v, want %v", tc.info, got, tc.want) + } + }) + } +} + func TestChpasswdStdinPreservesShellMetacharacters(t *testing.T) { password := `pa'";$(touch /tmp/pwned); echo #\\word` got, err := chpasswdStdin("root", password) diff --git a/install.sh b/install.sh index be62333..09f3787 100644 --- a/install.sh +++ b/install.sh @@ -1326,7 +1326,9 @@ setup_runtime_services() { libvirt_network_active() { - virsh net-info default 2>/dev/null | awk -F: 'tolower($1) ~ /^[[:space:]]*active[[:space:]]*$/ {gsub(/^[ \t]+|[ \t]+$/, "", $2); print tolower($2)}' | grep -qx yes + LC_ALL=C LANG=C virsh net-info default 2>/dev/null \ + | awk -F: '$1 ~ /^[[:space:]]*Active[[:space:]]*$/ {gsub(/^[ \t]+|[ \t]+$/, "", $2); print tolower($2)}' \ + | grep -qx yes } setup_default_libvirt_network() { @@ -1355,7 +1357,13 @@ EOF touch "$LIBVIRT_DEFAULT_MARKER" fi if ! libvirt_network_active; then - virsh net-start default + if ! start_output="$(LC_ALL=C LANG=C virsh net-start default 2>&1)"; then + # Another process may have activated the network after our check. + if ! libvirt_network_active; then + printf '%s\n' "$start_output" >&2 + die "libvirt default 网络仍未启动。请执行 virsh net-info default 查看详情。" + fi + fi fi virsh net-autostart default >/dev/null if ! libvirt_network_active; then