work on stable version

This commit is contained in:
Ryan Ward 2025-03-28 11:09:50 -04:00
parent 70b35932cb
commit a0f6f3a6a0
8 changed files with 96 additions and 109 deletions

59
main.go
View File

@ -12,8 +12,6 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"runtime"
"syscall"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/melbahja/goph" "github.com/melbahja/goph"
@ -34,8 +32,7 @@ type Credentials struct {
type Controller struct { type Controller struct {
cancel context.CancelFunc cancel context.CancelFunc
Stdout *bytes.Buffer cmd *exec.Cmd
Stderr *bytes.Buffer
} }
func (c *Controller) Stop() { func (c *Controller) Stop() {
@ -63,41 +60,35 @@ func StartMachine(machine types.Machine) *Controller {
ctrl := &Controller{} ctrl := &Controller{}
go func() { go func() {
command := machine.Expand()
ctx, cfunc := context.WithCancel(context.TODO()) ctx, cfunc := context.WithCancel(context.TODO())
ctrl.cancel = cfunc ctrl.cancel = cfunc
var stdout bytes.Buffer var stdout bytes.Buffer
var stderr bytes.Buffer var stderr bytes.Buffer
var cmd *exec.Cmd cmd := exec.Command(fmt.Sprintf("qemu-system-%v", machine.Arch), machine.Expand()...)
if runtime.GOOS == "windows" { fmt.Println(cmd.String())
cmd = exec.Command("powershell.exe", command) cmd.Stdout = &stderr
} else { cmd.Stderr = &stdout
cmd = exec.Command("bash", "-c", command) ctrl.cmd = cmd
}
cmd.Stdout = &stdout
cmd.Stderr = &stderr
ctrl.Stderr = &stderr
ctrl.Stdout = &stdout
cmd.SysProcAttr = &syscall.SysProcAttr{}
err := cmd.Start() err := cmd.Start()
if err != nil { if err != nil {
panic(err) fmt.Println("Error:", err)
} }
fmt.Println("running...", cmd.Process.Pid) go func() {
err := cmd.Wait()
//syscall.Kill(cmd.Process.Pid, syscall.SIGTERM) if err != nil {
fmt.Println("Error:", err)
}
cfunc()
}()
for { for {
select { select {
case <-ctx.Done(): case <-ctx.Done():
fmt.Println("Killing Process") fmt.Println("Killing Process")
//err := KillProcess(string(machine.Arch))
//err := cmd.Process.Signal(syscall.SIGTERM)
err := cmd.Process.Kill() err := cmd.Process.Kill()
if err != nil { if err != nil {
panic(err) fmt.Println("Error:", err)
} }
return return
default:
} }
} }
}() }()
@ -144,28 +135,16 @@ func main() {
NoGraphic: types.Set, NoGraphic: types.Set,
HardDiskA: `./imgs/UbuntuTest.img`, HardDiskA: `./imgs/UbuntuTest.img`,
} }
// ctrl := StartMachine(machine) 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()
fmt.Println("Press enter to stop machine") fmt.Println("Press enter to stop machine")
reader := bufio.NewReader(os.Stdin) reader := bufio.NewReader(os.Stdin)
_, _ = reader.ReadString('\n') _, _ = reader.ReadString('\n')
cmd.Process.Kill()
// ctrl.Stop() ctrl.Stop()
fmt.Println("Press enter to exit") fmt.Println("Press enter to exit")
_, _ = reader.ReadString('\n') _, _ = reader.ReadString('\n')
// if errout != "" {
// panic(fmt.Errorf("%v", errout))
// }
// if err != nil {
// fmt.Println(err)
// }
// Start new ssh connection with private key. // Start new ssh connection with private key.
//client, err := goph.New("baseimguser", "127.0.0.1", goph.Password("food99")) //client, err := goph.New("baseimguser", "127.0.0.1", goph.Password("food99"))

View File

@ -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

View File

@ -1,4 +1,11 @@
package types package accel
type (
VMExit string
Accelerator string
Thread string
KernelIrqchip string
)
const ( const (
KVM Accelerator = "kvm" KVM Accelerator = "kvm"
@ -22,7 +29,7 @@ const (
) )
type Accel struct { type Accel struct {
Accelerator Accelerator `json:"accel,omitempty"` Accelerator Accelerator `json:"accel,omitempty" omit:"tag"`
// enable Xen integrated Intel graphics passthrough, default=off // enable Xen integrated Intel graphics passthrough, default=off
IGDPassthrough *bool `json:"igd-passthru,omitempty"` IGDPassthrough *bool `json:"igd-passthru,omitempty"`
// controls accelerated irqchip support (default=on // controls accelerated irqchip support (default=on

View File

@ -1,4 +1,4 @@
package types package boot
// floppy (a), hard disk (c), CD-ROM (d), network (n) // floppy (a), hard disk (c), CD-ROM (d), network (n)
type Drives string type Drives string
@ -20,7 +20,7 @@ const (
type Boot struct { type Boot struct {
// Order of boot // Order of boot
Order []Drives `json:"order,omitempty"` Order []Drives `json:"order,omitempty" omit:"tag"`
Once []Drives `json:"once,omitempty"` Once []Drives `json:"once,omitempty"`
Menu *bool `json:"menu,omitempty"` Menu *bool `json:"menu,omitempty"`
// The file's name that would be passed to bios as logo picture, if menu=on // The file's name that would be passed to bios as logo picture, if menu=on

View File

@ -6,6 +6,8 @@ import (
"strings" "strings"
"github.com/fatih/structs" "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" "github.com/rayaman/go-qemu/pkg/types/numa"
) )
@ -30,14 +32,14 @@ type Machine struct {
// Amount of memory in MB // Amount of memory in MB
Memory Memory `json:"m,omitempty"` Memory Memory `json:"m,omitempty"`
// Number of CPU cores // Number of CPU cores
Cores SMP `json:"smp,omitempty"` Cores SMP `json:"smp,omitempty"`
Cpu CHIP `json:"cpu,omitempty"` Cpu CHIP `json:"cpu,omitempty"`
Accel Accel `json:"accel,omitempty"` Accel accel.Accel `json:"accel,omitempty"`
Boot Boot `json:"boot,omitempty"` Boot boot.Boot `json:"boot,omitempty"`
Numa numa.Numa `json:"numa,omitempty" omit:"true"` Numa numa.Numa `json:"numa,omitempty" omit:"true"`
MemoryPath string `json:"memory-path,omitempty"` MemoryPath string `json:"memory-path,omitempty"`
MemoryPrealloc Flag `json:"memory-prealloc,omitempty"` MemoryPrealloc Flag `json:"memory-prealloc,omitempty"`
Nic NIC `json:"nic,omitempty"` Nic NIC `json:"nic,omitempty"`
// Graphics // Graphics
NoGraphic Flag `json:"nographic,omitempty"` NoGraphic Flag `json:"nographic,omitempty"`
@ -52,7 +54,16 @@ type Machine struct {
CDROM string `json:"cdrom,omitempty"` 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) fields := structs.Fields(m)
exp := []string{} exp := []string{}
for _, field := range fields { for _, field := range fields {
@ -61,29 +72,30 @@ func (m *Machine) Expand() string {
if tag != "" { if tag != "" {
if field.Kind() == reflect.Struct || field.Kind() == reflect.Interface && !field.IsZero() { if field.Kind() == reflect.Struct || field.Kind() == reflect.Interface && !field.IsZero() {
if omit != "" { if omit != "" {
exp = append(exp, Expand(field.Value())) exp = append(exp, Expand(field.Value())...)
} else { } else {
exp = append(exp, Expand(field.Value(), tag)) exp = append(exp, Expand(field.Value(), tag)...)
} }
} else { } else {
if !field.IsZero() { if !field.IsZero() {
if strings.Contains(fmt.Sprintf("%v", field.Value()), " ") { if fmt.Sprintf("%v", field.Value()) == "flag-on" {
exp = append(exp, fmt.Sprintf(`-%v "%v"`, tag, field.Value())) exp = append(exp, "-"+tag)
} else { } 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{} opts := []string{}
fields := structs.Fields(obj) fields := structs.Fields(obj)
useSpace := false
for _, field := range fields { for _, field := range fields {
opt := strings.ReplaceAll(field.Tag("json"), ",omitempty", "") opt := strings.ReplaceAll(field.Tag("json"), ",omitempty", "")
opt = strings.ReplaceAll(opt, "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)) opts = append(opts, fmt.Sprintf("%v=%v-%v", opt, value.First, value.Last))
} }
case []numa.Node: case []numa.Node:
useSpace = true
for _, node := range value { for _, node := range value {
opts = append(opts, "-numa "+opt+","+Expand(node)) opts = append(opts, "-numa", opt)
opts = append(opts, Expand(node)...)
} }
case []numa.Dist: case []numa.Dist:
useSpace = true
for _, dist := range value { for _, dist := range value {
opts = append(opts, "-numa "+opt+","+Expand(dist)) opts = append(opts, "-numa", opt)
opts = append(opts, Expand(dist)...)
} }
case []numa.CPU: case []numa.CPU:
useSpace = true
for _, cpu := range value { for _, cpu := range value {
opts = append(opts, "-numa "+opt+","+Expand(cpu)) opts = append(opts, "-numa", opt)
opts = append(opts, Expand(cpu)...)
} }
case []numa.HMATLB: case []numa.HMATLB:
useSpace = true
for _, hmatlb := range value { for _, hmatlb := range value {
opts = append(opts, "-numa "+opt+","+Expand(hmatlb)) opts = append(opts, "-numa", opt)
opts = append(opts, Expand(hmatlb)...)
} }
case []numa.HMATCache: case []numa.HMATCache:
useSpace = true
for _, hmatcache := range value { for _, hmatcache := range value {
opts = append(opts, "-numa "+opt+","+Expand(hmatcache)) opts = append(opts, "-numa", opt)
opts = append(opts, Expand(hmatcache)...)
} }
case Options: case Options:
opts = append(opts, value.ExpandOptions()) opts = append(opts, value.ExpandOptions()...)
case []Drives: case []boot.Drives:
opts = append(opts, fmt.Sprintf("%v=%v", opt, strings.Join(ConvertDrives(value), ","))) 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: default:
if omit == "" { if omit == "" {
if strings.Contains(fmt.Sprintf("%v", value), " ") { opts = append(opts, fmt.Sprintf("%v=%v", opt, value))
opts = append(opts, fmt.Sprintf(`%v="%v"`, opt, value)) } else if omit == "tag" {
} else { opts = append(opts, fmt.Sprintf("%v", value))
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))
}
} }
} }
} }
} }
otag := ""
if len(tag) > 0 { 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, ",")
} }

View File

@ -1,9 +1,13 @@
package types package types
import "strings"
type ( type (
NICType string NICType string
) )
// TODO: Implement other types user,socket,brtidge
const ( const (
TAP NICType = "tap" TAP NICType = "tap"
Bridge NICType = "bridge" Bridge NICType = "bridge"
@ -16,22 +20,22 @@ type TapOptions struct {
IFName string `json:"ifname,omitempty"` IFName string `json:"ifname,omitempty"`
} }
func (t *TapOptions) ExpandOptions() string { func (t *TapOptions) ExpandOptions() []string {
return Expand(t) return []string{"tap," + strings.Join(Expand(t), ",")}
} }
type BridgeOptions struct { type BridgeOptions struct {
} }
func (b *BridgeOptions) ExpandOptions() string { func (b *BridgeOptions) ExpandOptions() []string {
return "" return []string{}
} }
type UserOptions struct { type UserOptions struct {
} }
func (u *UserOptions) ExpandOptions() string { func (u *UserOptions) ExpandOptions() []string {
return "" return []string{}
} }
type SocketOptions struct { type SocketOptions struct {
@ -44,12 +48,12 @@ type SocketOptions struct {
Connect string `json:"connect,omitempty"` Connect string `json:"connect,omitempty"`
} }
func (s *SocketOptions) ExpandOptions() string { func (s *SocketOptions) ExpandOptions() []string {
return Expand(s) return []string{}
} }
type Options interface { type Options interface {
ExpandOptions() string ExpandOptions() []string
} }
type NIC struct { type NIC struct {

View File

@ -16,7 +16,7 @@ Note: Different machines may have different subsets of the CPU topology
*/ */
type SMP struct { type SMP struct {
// The number of initial CPUs // 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 // Maximum number of total CPUs, including offline CPUs for hotplug, etc
MaxCpus uint `json:"maxcpus,omitempty"` MaxCpus uint `json:"maxcpus,omitempty"`
// Number of drawers on the machine board // Number of drawers on the machine board

View File

@ -8,10 +8,6 @@ type (
CompressionType string CompressionType string
Format string Format string
System string System string
Accelerator string
VMExit string
Thread string
KernelIrqchip string
Flag string Flag string
) )