114 lines
2.6 KiB
Go
114 lines
2.6 KiB
Go
//go:build linux
|
|
|
|
package qemu
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
type DiskConfig struct {
|
|
Path string
|
|
Dev string
|
|
}
|
|
|
|
type Config struct {
|
|
Name string
|
|
TapID int
|
|
Mac string
|
|
Disks []DiskConfig
|
|
Memory int
|
|
CPUs int
|
|
UEFICodePath string
|
|
UEFIVarsPath string
|
|
SerialDir string
|
|
MonitorDir string
|
|
QMPDir string
|
|
}
|
|
|
|
func Start(cfg Config) error {
|
|
memory := cfg.Memory
|
|
if memory == 0 {
|
|
memory = 512
|
|
}
|
|
|
|
cpus := cfg.CPUs
|
|
if cpus == 0 {
|
|
cpus = 1
|
|
}
|
|
|
|
for _, dir := range []string{cfg.SerialDir, cfg.MonitorDir, cfg.QMPDir} {
|
|
if dir != "" {
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
return fmt.Errorf("mkdir %s: %w", dir, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
serialSock := filepath.Join(cfg.SerialDir, cfg.Name+".sock")
|
|
monitorSock := filepath.Join(cfg.MonitorDir, cfg.Name+".sock")
|
|
qmpSock := filepath.Join(cfg.QMPDir, cfg.Name+".sock")
|
|
|
|
args := []string{
|
|
"-enable-kvm",
|
|
"-cpu", "host",
|
|
"-m", fmt.Sprintf("%d", memory),
|
|
"-smp", fmt.Sprintf("%d", cpus),
|
|
"-serial", fmt.Sprintf("unix:%s,server,nowait", serialSock),
|
|
"-monitor", fmt.Sprintf("unix:%s,server,nowait", monitorSock),
|
|
"-qmp", fmt.Sprintf("unix:%s,server,nowait", qmpSock),
|
|
"-display", "none",
|
|
}
|
|
|
|
if cfg.UEFICodePath != "" && cfg.UEFIVarsPath != "" {
|
|
args = append(args,
|
|
"-drive", fmt.Sprintf("if=pflash,format=raw,readonly=on,file=%s", cfg.UEFICodePath),
|
|
"-drive", fmt.Sprintf("if=pflash,format=raw,file=%s", cfg.UEFIVarsPath),
|
|
)
|
|
}
|
|
|
|
hasScsi := false
|
|
for _, d := range cfg.Disks {
|
|
if strings.HasPrefix(d.Dev, "sd") {
|
|
hasScsi = true
|
|
break
|
|
}
|
|
}
|
|
if hasScsi {
|
|
args = append(args, "-device", "virtio-scsi-pci,id=scsi0")
|
|
}
|
|
|
|
sorted := make([]DiskConfig, len(cfg.Disks))
|
|
copy(sorted, cfg.Disks)
|
|
sort.Slice(sorted, func(i, j int) bool { return sorted[i].Dev < sorted[j].Dev })
|
|
|
|
for _, d := range sorted {
|
|
if strings.HasPrefix(d.Dev, "sd") {
|
|
scsiID := int(d.Dev[2] - 'a')
|
|
args = append(args,
|
|
"-drive", fmt.Sprintf("file=%s,if=none,id=%s", d.Path, d.Dev),
|
|
"-device", fmt.Sprintf("scsi-hd,drive=%s,bus=scsi0.0,scsi-id=%d", d.Dev, scsiID),
|
|
)
|
|
} else {
|
|
args = append(args,
|
|
"-drive", fmt.Sprintf("file=%s,if=virtio,id=%s", d.Path, d.Dev),
|
|
)
|
|
}
|
|
}
|
|
|
|
args = append(args,
|
|
"-netdev", fmt.Sprintf("tap,id=net0,ifname=tap%d,script=no,downscript=no", cfg.TapID),
|
|
"-device", fmt.Sprintf("virtio-net-pci,netdev=net0,mac=%s", cfg.Mac),
|
|
"-daemonize",
|
|
)
|
|
|
|
cmd := exec.Command("qemu-system-x86_64", args...)
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("qemu-system-x86_64: %w", err)
|
|
}
|
|
return nil
|
|
}
|