From d404c252190eee1ede735b91102466bf2b154ba9 Mon Sep 17 00:00:00 2001 From: markmental Date: Mon, 15 Dec 2025 16:01:02 -0500 Subject: [PATCH] Add root check and prettier tables --- main.go | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 5 deletions(-) diff --git a/main.go b/main.go index 975ac4f..8d9dec8 100644 --- a/main.go +++ b/main.go @@ -54,6 +54,13 @@ var osImages = map[string]OSImage{ }, } +func requireRoot() { + if os.Geteuid() != 0 { + fmt.Fprintln(os.Stderr, "mnmivm must be run as root. Try: `sudo mnmivm` or login as the root user") + os.Exit(1) + } +} + func ensureDirs() { for _, d := range []string{imageDir, vmDirBase} { if err := os.MkdirAll(d, 0755); err != nil { @@ -176,6 +183,64 @@ disable_root: true } } +func renderBoxTable(headers []string, rows [][]string) { + colWidths := make([]int, len(headers)) + + // compute column widths + for i, h := range headers { + colWidths[i] = len(h) + } + for _, row := range rows { + for i, cell := range row { + if len(cell) > colWidths[i] { + colWidths[i] = len(cell) + } + } + } + + // helpers + repeat := func(s string, n int) string { + return strings.Repeat(s, n) + } + + drawRow := func(left, mid, right string) { + fmt.Print(left) + for i, w := range colWidths { + fmt.Print(repeat("─", w+2)) + if i < len(colWidths)-1 { + fmt.Print(mid) + } + } + fmt.Println(right) + } + + // top border + drawRow("┌", "┬", "┐") + + // header row + fmt.Print("│") + for i, h := range headers { + fmt.Printf(" %-*s │", colWidths[i], h) + } + fmt.Println() + + // header separator + drawRow("├", "┼", "┤") + + // data rows + for _, row := range rows { + fmt.Print("│") + for i, cell := range row { + fmt.Printf(" %-*s │", colWidths[i], cell) + } + fmt.Println() + } + + // bottom border + drawRow("└", "┴", "┘") +} + + func createVM(name, osName, pubKeyPath string) { if _, ok := osImages[osName]; !ok { panic("unsupported OS: " + osName) @@ -298,9 +363,16 @@ func vmRunning(name string) bool { func listVMs() { entries, _ := os.ReadDir(vmDirBase) - fmt.Printf("%-12s %-8s %-8s %-16s %-16s %s\n", - "NAME", "STATE", "OS", "SSH", "VNC", "PUBKEY") - fmt.Println(strings.Repeat("-", 90)) + headers := []string{ + "NAME", + "STATE", + "OS", + "SSH", + "VNC", + "PUBKEY", + } + + rows := [][]string{} for _, e := range entries { name := e.Name() @@ -312,6 +384,7 @@ func listVMs() { } osName := readFileTrim(filepath.Join(dir, "os.name")) + sshPort := readFileTrim(filepath.Join(dir, "ssh.port")) vncPort := readFileTrim(filepath.Join(dir, "vnc.port")) @@ -325,12 +398,27 @@ func listVMs() { vnc = "0.0.0.0:" + vncPort } - fmt.Printf("%-12s %-8s %-8s %-16s %-16s %s\n", - name, state, osName, ssh, vnc, vmPubKeyPath(name)) + rows = append(rows, []string{ + name, + state, + osName, + ssh, + vnc, + vmPubKeyPath(name), + }) } + + if len(rows) == 0 { + fmt.Println("No VMs found.") + return + } + + renderBoxTable(headers, rows) } + func main() { + requireRoot() if len(os.Args) < 2 { usage() }