diff --git a/main.go b/main.go index e4a9c47..bcbf2a5 100644 --- a/main.go +++ b/main.go @@ -12,8 +12,6 @@ import ( "os" "os/exec" "path/filepath" - "runtime" - "syscall" "github.com/google/uuid" "github.com/melbahja/goph" @@ -34,8 +32,7 @@ type Credentials struct { type Controller struct { cancel context.CancelFunc - Stdout *bytes.Buffer - Stderr *bytes.Buffer + cmd *exec.Cmd } func (c *Controller) Stop() { @@ -63,41 +60,35 @@ func StartMachine(machine types.Machine) *Controller { ctrl := &Controller{} go func() { - command := machine.Expand() ctx, cfunc := context.WithCancel(context.TODO()) ctrl.cancel = cfunc var stdout bytes.Buffer var stderr bytes.Buffer - var cmd *exec.Cmd - if runtime.GOOS == "windows" { - cmd = exec.Command("powershell.exe", command) - } else { - cmd = exec.Command("bash", "-c", command) - } - cmd.Stdout = &stdout - cmd.Stderr = &stderr - ctrl.Stderr = &stderr - ctrl.Stdout = &stdout - cmd.SysProcAttr = &syscall.SysProcAttr{} + cmd := exec.Command(fmt.Sprintf("qemu-system-%v", machine.Arch), machine.Expand()...) + fmt.Println(cmd.String()) + cmd.Stdout = &stderr + cmd.Stderr = &stdout + ctrl.cmd = cmd err := cmd.Start() if err != nil { - panic(err) + fmt.Println("Error:", err) } - fmt.Println("running...", cmd.Process.Pid) - - //syscall.Kill(cmd.Process.Pid, syscall.SIGTERM) + go func() { + err := cmd.Wait() + if err != nil { + fmt.Println("Error:", err) + } + cfunc() + }() for { select { case <-ctx.Done(): fmt.Println("Killing Process") - //err := KillProcess(string(machine.Arch)) - //err := cmd.Process.Signal(syscall.SIGTERM) err := cmd.Process.Kill() if err != nil { - panic(err) + fmt.Println("Error:", err) } return - default: } } }() @@ -144,28 +135,16 @@ func main() { NoGraphic: types.Set, HardDiskA: `./imgs/UbuntuTest.img`, } - // ctrl := StartMachine(machine) - command := machine.Expand() - fmt.Println(command) - // this works, we need to format like this - cmd := exec.Command("qemu-system-x86_64", "-m", "size=4096", "-smp", "cpus=4", "-accel", "accel=whpx", "-boot", "order=c,menu=off", "-nic", "tap,ifname=qemu-tap", "-nographic", "-hda", "./imgs/UbuntuTest.img") - cmd.Start() + ctrl := StartMachine(machine) + fmt.Println("Press enter to stop machine") reader := bufio.NewReader(os.Stdin) _, _ = reader.ReadString('\n') - cmd.Process.Kill() - // ctrl.Stop() + + ctrl.Stop() fmt.Println("Press enter to exit") _, _ = reader.ReadString('\n') - // if errout != "" { - // panic(fmt.Errorf("%v", errout)) - // } - - // if err != nil { - // fmt.Println(err) - // } - // Start new ssh connection with private key. //client, err := goph.New("baseimguser", "127.0.0.1", goph.Password("food99")) diff --git a/pkg/system/machine/machine.go b/pkg/system/machine/machine.go deleted file mode 100644 index f9b5378..0000000 --- a/pkg/system/machine/machine.go +++ /dev/null @@ -1,3 +0,0 @@ -package machine - -// qemu-system-x86_64.exe -m 4096 -accel whpx -boot d -smp 4 -net nic -net user,hostfwd=tcp::8888-:22 -hda .\imgs\ubuntu_server_test.img -nographic diff --git a/pkg/types/accel.go b/pkg/types/accel/accel.go similarity index 90% rename from pkg/types/accel.go rename to pkg/types/accel/accel.go index 43acf1c..b8013dc 100644 --- a/pkg/types/accel.go +++ b/pkg/types/accel/accel.go @@ -1,4 +1,11 @@ -package types +package accel + +type ( + VMExit string + Accelerator string + Thread string + KernelIrqchip string +) const ( KVM Accelerator = "kvm" @@ -22,7 +29,7 @@ const ( ) type Accel struct { - Accelerator Accelerator `json:"accel,omitempty"` + Accelerator Accelerator `json:"accel,omitempty" omit:"tag"` // enable Xen integrated Intel graphics passthrough, default=off IGDPassthrough *bool `json:"igd-passthru,omitempty"` // controls accelerated irqchip support (default=on diff --git a/pkg/types/boot.go b/pkg/types/boot/boot.go similarity index 92% rename from pkg/types/boot.go rename to pkg/types/boot/boot.go index 43712af..fe56f30 100644 --- a/pkg/types/boot.go +++ b/pkg/types/boot/boot.go @@ -1,4 +1,4 @@ -package types +package boot // floppy (a), hard disk (c), CD-ROM (d), network (n) type Drives string @@ -20,7 +20,7 @@ const ( type Boot struct { // Order of boot - Order []Drives `json:"order,omitempty"` + Order []Drives `json:"order,omitempty" omit:"tag"` Once []Drives `json:"once,omitempty"` Menu *bool `json:"menu,omitempty"` // The file's name that would be passed to bios as logo picture, if menu=on diff --git a/pkg/types/machine.go b/pkg/types/machine.go index bfdfae8..d94f20f 100644 --- a/pkg/types/machine.go +++ b/pkg/types/machine.go @@ -6,6 +6,8 @@ import ( "strings" "github.com/fatih/structs" + "github.com/rayaman/go-qemu/pkg/types/accel" + "github.com/rayaman/go-qemu/pkg/types/boot" "github.com/rayaman/go-qemu/pkg/types/numa" ) @@ -30,14 +32,14 @@ type Machine struct { // Amount of memory in MB Memory Memory `json:"m,omitempty"` // Number of CPU cores - Cores SMP `json:"smp,omitempty"` - Cpu CHIP `json:"cpu,omitempty"` - Accel Accel `json:"accel,omitempty"` - Boot Boot `json:"boot,omitempty"` - Numa numa.Numa `json:"numa,omitempty" omit:"true"` - MemoryPath string `json:"memory-path,omitempty"` - MemoryPrealloc Flag `json:"memory-prealloc,omitempty"` - Nic NIC `json:"nic,omitempty"` + Cores SMP `json:"smp,omitempty"` + Cpu CHIP `json:"cpu,omitempty"` + Accel accel.Accel `json:"accel,omitempty"` + Boot boot.Boot `json:"boot,omitempty"` + Numa numa.Numa `json:"numa,omitempty" omit:"true"` + MemoryPath string `json:"memory-path,omitempty"` + MemoryPrealloc Flag `json:"memory-prealloc,omitempty"` + Nic NIC `json:"nic,omitempty"` // Graphics NoGraphic Flag `json:"nographic,omitempty"` @@ -52,7 +54,16 @@ type Machine struct { CDROM string `json:"cdrom,omitempty"` } -func (m *Machine) Expand() string { +func remove(s []string, r string) []string { + for i, v := range s { + if v == r { + return append(s[:i], s[i+1:]...) + } + } + return s +} + +func (m *Machine) Expand() []string { fields := structs.Fields(m) exp := []string{} for _, field := range fields { @@ -61,29 +72,30 @@ func (m *Machine) Expand() string { if tag != "" { if field.Kind() == reflect.Struct || field.Kind() == reflect.Interface && !field.IsZero() { if omit != "" { - exp = append(exp, Expand(field.Value())) + exp = append(exp, Expand(field.Value())...) } else { - exp = append(exp, Expand(field.Value(), tag)) + exp = append(exp, Expand(field.Value(), tag)...) } } else { if !field.IsZero() { - if strings.Contains(fmt.Sprintf("%v", field.Value()), " ") { - exp = append(exp, fmt.Sprintf(`-%v "%v"`, tag, field.Value())) + if fmt.Sprintf("%v", field.Value()) == "flag-on" { + exp = append(exp, "-"+tag) } else { - exp = append(exp, fmt.Sprintf("-%v %v", tag, field.Value())) + exp = append(exp, "-"+tag, fmt.Sprintf("%v", field.Value())) } } } } } - return strings.ReplaceAll(strings.ReplaceAll(fmt.Sprintf("qemu-system-%v %v", m.Arch, strings.Join(exp, " ")), " flag-on", ""), " ", " ") + exp = remove(exp, "") + return exp } -func Expand(obj any, tag ...string) string { +func Expand(obj any, tag ...string) []string { opts := []string{} fields := structs.Fields(obj) - useSpace := false + for _, field := range fields { opt := strings.ReplaceAll(field.Tag("json"), ",omitempty", "") opt = strings.ReplaceAll(opt, "omitempty", "") @@ -103,58 +115,50 @@ func Expand(obj any, tag ...string) string { opts = append(opts, fmt.Sprintf("%v=%v-%v", opt, value.First, value.Last)) } case []numa.Node: - useSpace = true for _, node := range value { - opts = append(opts, "-numa "+opt+","+Expand(node)) + opts = append(opts, "-numa", opt) + opts = append(opts, Expand(node)...) } case []numa.Dist: - useSpace = true for _, dist := range value { - opts = append(opts, "-numa "+opt+","+Expand(dist)) + opts = append(opts, "-numa", opt) + opts = append(opts, Expand(dist)...) } case []numa.CPU: - useSpace = true for _, cpu := range value { - opts = append(opts, "-numa "+opt+","+Expand(cpu)) + opts = append(opts, "-numa", opt) + opts = append(opts, Expand(cpu)...) } case []numa.HMATLB: - useSpace = true for _, hmatlb := range value { - opts = append(opts, "-numa "+opt+","+Expand(hmatlb)) + opts = append(opts, "-numa", opt) + opts = append(opts, Expand(hmatlb)...) } case []numa.HMATCache: - useSpace = true for _, hmatcache := range value { - opts = append(opts, "-numa "+opt+","+Expand(hmatcache)) + opts = append(opts, "-numa", opt) + opts = append(opts, Expand(hmatcache)...) } case Options: - opts = append(opts, value.ExpandOptions()) - case []Drives: - opts = append(opts, fmt.Sprintf("%v=%v", opt, strings.Join(ConvertDrives(value), ","))) + opts = append(opts, value.ExpandOptions()...) + case []boot.Drives: + if omit == "tag" { + opts = append(opts, fmt.Sprintf("%v", strings.Join(boot.ConvertDrives(value), ","))) + } else { + opts = append(opts, fmt.Sprintf("%v=%v", opt, strings.Join(boot.ConvertDrives(value), ","))) + } default: if omit == "" { - if strings.Contains(fmt.Sprintf("%v", value), " ") { - opts = append(opts, fmt.Sprintf(`%v="%v"`, opt, value)) - } else { - opts = append(opts, fmt.Sprintf("%v=%v", opt, value)) - } - } else { - if strings.Contains(fmt.Sprintf("%v", value), " ") { - opts = append(opts, fmt.Sprintf(`"%v"`, value)) - } else { - opts = append(opts, fmt.Sprintf("%v", value)) - } + opts = append(opts, fmt.Sprintf("%v=%v", opt, value)) + } else if omit == "tag" { + opts = append(opts, fmt.Sprintf("%v", value)) } - } } } - otag := "" if len(tag) > 0 { - otag = "-" + tag[0] + " " + return []string{"-" + tag[0], strings.Join(opts, ",")} + } else { + return []string{strings.Join(opts, ",")} } - if useSpace { - return otag + strings.Join(opts, " ") - } - return otag + strings.Join(opts, ",") } diff --git a/pkg/types/nic.go b/pkg/types/nic.go index 777d708..3999ca4 100644 --- a/pkg/types/nic.go +++ b/pkg/types/nic.go @@ -1,9 +1,13 @@ package types +import "strings" + type ( NICType string ) +// TODO: Implement other types user,socket,brtidge + const ( TAP NICType = "tap" Bridge NICType = "bridge" @@ -16,22 +20,22 @@ type TapOptions struct { IFName string `json:"ifname,omitempty"` } -func (t *TapOptions) ExpandOptions() string { - return Expand(t) +func (t *TapOptions) ExpandOptions() []string { + return []string{"tap," + strings.Join(Expand(t), ",")} } type BridgeOptions struct { } -func (b *BridgeOptions) ExpandOptions() string { - return "" +func (b *BridgeOptions) ExpandOptions() []string { + return []string{} } type UserOptions struct { } -func (u *UserOptions) ExpandOptions() string { - return "" +func (u *UserOptions) ExpandOptions() []string { + return []string{} } type SocketOptions struct { @@ -44,12 +48,12 @@ type SocketOptions struct { Connect string `json:"connect,omitempty"` } -func (s *SocketOptions) ExpandOptions() string { - return Expand(s) +func (s *SocketOptions) ExpandOptions() []string { + return []string{} } type Options interface { - ExpandOptions() string + ExpandOptions() []string } type NIC struct { diff --git a/pkg/types/smp.go b/pkg/types/smp.go index f339633..5c636d7 100644 --- a/pkg/types/smp.go +++ b/pkg/types/smp.go @@ -16,7 +16,7 @@ Note: Different machines may have different subsets of the CPU topology */ type SMP struct { // The number of initial CPUs - Cpus uint `json:"cpus,omitempty"` + Cpus uint `json:"cpus,omitempty" omit:"tag"` // Maximum number of total CPUs, including offline CPUs for hotplug, etc MaxCpus uint `json:"maxcpus,omitempty"` // Number of drawers on the machine board diff --git a/pkg/types/types.go b/pkg/types/types.go index 80a0dfb..f4dbc00 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -8,10 +8,6 @@ type ( CompressionType string Format string System string - Accelerator string - VMExit string - Thread string - KernelIrqchip string Flag string )