diff --git a/spread/qemu.go b/spread/qemu.go index 20f0bdb0..32a00a26 100644 --- a/spread/qemu.go +++ b/spread/qemu.go @@ -6,6 +6,7 @@ import ( "os" "os/exec" "path/filepath" + "regexp" "strconv" "golang.org/x/net/context" @@ -121,7 +122,30 @@ func biosPath(biosName string) (string, error) { return "", fmt.Errorf("cannot find bios path for %q", biosName) } +func machineType(system *System) (string, error) { + // Utilise the variable to determine which QEMU machine type should be + // used. Use the "pc" QEMU machine type if nothing is specified. + if system.Plan == "" { + system.Plan = "pc" + } + + // Make sure that the value for the machine type is sane and does not + // allow the user to pass arbitrary arguments to QEMU by ensuring that + // the value is a single word. + r := regexp.MustCompile(`^\S+$`) + if !r.Match([]byte(system.Plan)) { + return "", fmt.Errorf(`invalid machine type: "%s"`, system.Plan) + } + + return system.Plan, nil +} + func qemuCmd(system *System, path string, mem, port int) (*exec.Cmd, error) { + machine, err := machineType(system) + if err != nil { + return nil, err + } + serial := fmt.Sprintf("telnet:127.0.0.1:%d,server,nowait", port+100) monitor := fmt.Sprintf("telnet:127.0.0.1:%d,server,nowait", port+200) fwd := fmt.Sprintf("user,hostfwd=tcp:127.0.0.1:%d-:22", port) @@ -133,6 +157,7 @@ func qemuCmd(system *System, path string, mem, port int) (*exec.Cmd, error) { "-net", fwd, "-serial", serial, "-monitor", monitor, + "-M", machine, path) if os.Getenv("SPREAD_QEMU_GUI") != "1" { cmd.Args = append([]string{cmd.Args[0], "-nographic"}, cmd.Args[1:]...) diff --git a/spread/qemu_test.go b/spread/qemu_test.go index 619448ec..562bae68 100644 --- a/spread/qemu_test.go +++ b/spread/qemu_test.go @@ -1,6 +1,7 @@ package spread_test import ( + "fmt" "io/ioutil" "os" "path/filepath" @@ -91,3 +92,43 @@ func (s *qemuSuite) TestQemuCmdWithEfi(c *C) { c.Check(strings.Contains(s, ":-bios:/usr/share/OVMF/OVMF_CODE.fd:"), Equals, tc.UseBiosQemuOption) } } + +func (s *qemuSuite) TestQemuMachineTypes(c *C) { + imageName := "ubuntu-20.06-64" + + restore := makeMockQemuImg(c, imageName) + defer restore() + + tests := []struct { + machineType string + expectedMachineType string + expectedErr string + }{ + // Make sure the default is "pc" + {"", "pc", ""}, + // Make sure the specified machine type is used + {"q35", "q35", ""}, + // Do not allow multiple words + {"invalid machine", "", `invalid machine type: "invalid machine"`}, + } + + for _, tc := range tests { + ms := &spread.System{ + Name: "some-name", + Image: imageName, + Backend: "qemu", + Plan: tc.machineType, + } + cmd, err := spread.QemuCmd(ms, "/path/to/image", 512, 9999) + + if tc.expectedErr != "" { + c.Check(err, ErrorMatches, tc.expectedErr) + continue + } + c.Assert(err, IsNil) + + s := strings.Join(cmd.Args, " ") + // Make sure the QEMU command specifies the given machine type, e.g. "-M pc" + c.Assert(s, Matches, fmt.Sprintf("^.*-M %s.*$", tc.expectedMachineType)) + } +}