| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- 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
- }
|