package agent import ( "os" "os/exec" "github.com/creack/pty" ) // PTY is a pseudo-terminal running an interactive login shell. type PTY struct { cmd *exec.Cmd f *os.File } // StartPTY launches the shell attached to a new PTY. The agent runs as root // (systemd User=root), so this is a root shell by design. func StartPTY() (*PTY, error) { shell := "/bin/bash" if _, err := os.Stat(shell); err != nil { shell = "/bin/sh" } cmd := exec.Command(shell, "-l") cmd.Env = append(os.Environ(), "TERM=xterm-256color") f, err := pty.Start(cmd) if err != nil { return nil, err } return &PTY{cmd: cmd, f: f}, nil } // Read reads terminal output. func (p *PTY) Read(b []byte) (int, error) { return p.f.Read(b) } // Write writes keystrokes to the terminal. func (p *PTY) Write(b []byte) (int, error) { return p.f.Write(b) } // Resize changes the terminal window size. func (p *PTY) Resize(cols, rows uint16) { if cols == 0 || rows == 0 { return } _ = pty.Setsize(p.f, &pty.Winsize{Rows: rows, Cols: cols}) } // Close terminates the shell and releases the PTY. func (p *PTY) Close() error { err := p.f.Close() if p.cmd.Process != nil { _ = p.cmd.Process.Kill() } _ = p.cmd.Wait() return err }