package main import ( "bufio" "flag" "fmt" "io" "net" "os" "os/signal" "strings" "syscall" "time" "golang.org/x/crypto/ssh/terminal" ) func main() { addr := flag.String("addr", "127.0.0.1:8282", "DebTik CLI server address") username := flag.String("u", "", "Username for automatic authentication") password := flag.String("p", "", "Password for automatic authentication") cmdFlag := flag.String("c", "", "Execute a single command non-interactively and exit") flag.Parse() conn, err := net.DialTimeout("tcp", *addr, 3*time.Second) if err != nil { fmt.Fprintf(os.Stderr, "\033[31mError: Could not connect to DebTik CLI service on %s\033[0m\n", *addr) fmt.Fprintf(os.Stderr, "Ensure proxmox-router service is active and listening on port 8282.\n") os.Exit(1) } defer conn.Close() // If one-shot command execution is requested: if *cmdFlag != "" { runOneShot(conn, *username, *password, *cmdFlag) return } // Interactive Mode runInteractive(conn, *username, *password) } func runOneShot(conn net.Conn, username, password, command string) { reader := bufio.NewReader(conn) // If credentials were provided, handle initial prompt if username != "" && password != "" { _ = autoLogin(conn, reader, username, password) } // Send command followed by quit _, _ = fmt.Fprintf(conn, "%s\nquit\n", command) // Read until EOF _, _ = io.Copy(os.Stdout, reader) } func runInteractive(conn net.Conn, username, password string) { fd := int(os.Stdin.Fd()) isTerm := terminal.IsTerminal(fd) if isTerm { oldState, err := terminal.MakeRaw(fd) if err == nil { defer func() { _ = terminal.Restore(fd, oldState) }() } } // Setup clean signal handling to restore terminal on SIGINT/SIGTERM sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) go func() { <-sigChan if isTerm { _ = terminal.Restore(fd, nil) } os.Exit(0) }() errChan := make(chan error, 2) // Copy from Remote Server -> Local Terminal go func() { _, err := io.Copy(os.Stdout, conn) errChan <- err }() // Copy from Local Terminal -> Remote Server go func() { _, err := io.Copy(conn, os.Stdin) errChan <- err }() // Wait for connection to close <-errChan } func autoLogin(conn net.Conn, reader *bufio.Reader, username, password string) error { // Read until "login: " for { line, err := reader.ReadString(':') if err != nil { return err } if strings.Contains(strings.ToLower(line), "login") { _, _ = fmt.Fprintf(conn, "%s\n", username) break } } // Read until "Password: " for { line, err := reader.ReadString(':') if err != nil { return err } if strings.Contains(strings.ToLower(line), "password") { _, _ = fmt.Fprintf(conn, "%s\n", password) break } } return nil }