forked from gliderlabs/ssh
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: allocate real pty This adds a new PtyHandler to handle allocating PTYs and storing them in a platform specific field in `Pty`. This PR is backward-compatible, it defaults to EmulatePty handler that sets the `emulatePty` field in context and uses `PtyWriter` to preserve the current behavor. * fix: update pty godoc * fix: convert pty handlers to server options * fix: consume resize events on pty * feat: support windows conpty * feat: add pty start process example * fix: return tty name * fix: ptystart example for unix * fix: imports Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * fix: update Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * chore: deps --------- Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> Co-authored-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
- Loading branch information
1 parent
7e1d867
commit 7ed763a
Showing
12 changed files
with
876 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"log" | ||
"os" | ||
"os/exec" | ||
"runtime" | ||
"time" | ||
|
||
"github.com/charmbracelet/ssh" | ||
) | ||
|
||
func main() { | ||
ssh.Handle(func(s ssh.Session) { | ||
log.Printf("connected %s %s %q", s.User(), s.RemoteAddr(), s.RawCommand()) | ||
defer log.Printf("disconnected %s %s", s.User(), s.RemoteAddr()) | ||
|
||
pty, _, ok := s.Pty() | ||
if !ok { | ||
io.WriteString(s, "No PTY requested.\n") | ||
s.Exit(1) | ||
return | ||
} | ||
|
||
name := "bash" | ||
if runtime.GOOS == "windows" { | ||
name = "powershell.exe" | ||
} | ||
cmd := exec.Command(name) | ||
cmd.Env = append(os.Environ(), "SSH_TTY="+pty.Name(), fmt.Sprintf("TERM=%s", pty.Term)) | ||
if err := pty.Start(cmd); err != nil { | ||
fmt.Fprintln(s, err.Error()) | ||
s.Exit(1) | ||
return | ||
} | ||
|
||
if runtime.GOOS == "windows" { | ||
// ProcessState gets populated by pty.Start waiting on the process | ||
// to exit. | ||
for cmd.ProcessState == nil { | ||
time.Sleep(100 * time.Millisecond) | ||
} | ||
|
||
s.Exit(cmd.ProcessState.ExitCode()) | ||
} else { | ||
if err := cmd.Wait(); err != nil { | ||
fmt.Fprintln(s, err) | ||
s.Exit(cmd.ProcessState.ExitCode()) | ||
} | ||
} | ||
}) | ||
|
||
log.Println("starting ssh server on port 2222...") | ||
if err := ssh.ListenAndServe(":2222", nil, ssh.AllocatePty()); err != nil && err != ssh.ErrServerClosed { | ||
log.Fatal(err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
//go:build !linux && !darwin && !freebsd && !dragonfly && !netbsd && !openbsd && !solaris && !windows | ||
// +build !linux,!darwin,!freebsd,!dragonfly,!netbsd,!openbsd,!solaris,!windows | ||
|
||
package ssh | ||
|
||
import ( | ||
"os/exec" | ||
|
||
"golang.org/x/crypto/ssh" | ||
) | ||
|
||
type impl struct{} | ||
|
||
func (i *impl) IsZero() bool { | ||
return true | ||
} | ||
|
||
func (i *impl) Name() string { | ||
return "" | ||
} | ||
|
||
func (i *impl) Read(p []byte) (n int, err error) { | ||
return 0, ErrUnsupported | ||
} | ||
|
||
func (i *impl) Write(p []byte) (n int, err error) { | ||
return 0, ErrUnsupported | ||
} | ||
|
||
func (i *impl) Resize(w int, h int) error { | ||
return ErrUnsupported | ||
} | ||
|
||
func (i *impl) Close() error { | ||
return nil | ||
} | ||
|
||
func (*impl) start(*exec.Cmd) error { | ||
return ErrUnsupported | ||
} | ||
|
||
func newPty(Context, string, Window, ssh.TerminalModes) (impl, error) { | ||
return impl{}, ErrUnsupported | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris | ||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris | ||
|
||
package ssh | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"os/exec" | ||
"syscall" | ||
|
||
"github.com/creack/pty" | ||
"github.com/u-root/u-root/pkg/termios" | ||
"golang.org/x/crypto/ssh" | ||
"golang.org/x/sys/unix" | ||
) | ||
|
||
type impl struct { | ||
// Master is the master PTY file descriptor. | ||
Master *os.File | ||
|
||
// Slave is the slave PTY file descriptor. | ||
Slave *os.File | ||
} | ||
|
||
func (i *impl) IsZero() bool { | ||
return i.Master == nil && i.Slave == nil | ||
} | ||
|
||
// Name returns the name of the slave PTY. | ||
func (i *impl) Name() string { | ||
return i.Slave.Name() | ||
} | ||
|
||
// Read implements ptyInterface. | ||
func (i *impl) Read(p []byte) (n int, err error) { | ||
return i.Master.Read(p) | ||
} | ||
|
||
// Write implements ptyInterface. | ||
func (i *impl) Write(p []byte) (n int, err error) { | ||
return i.Master.Write(p) | ||
} | ||
|
||
func (i *impl) Close() error { | ||
if err := i.Master.Close(); err != nil { | ||
return err | ||
} | ||
return i.Slave.Close() | ||
} | ||
|
||
func (i *impl) Resize(w int, h int) (rErr error) { | ||
conn, err := i.Master.SyscallConn() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return conn.Control(func(fd uintptr) { | ||
rErr = termios.SetWinSize(fd, &termios.Winsize{ | ||
Winsize: unix.Winsize{ | ||
Row: uint16(h), | ||
Col: uint16(w), | ||
}, | ||
}) | ||
}) | ||
} | ||
|
||
func (i *impl) start(c *exec.Cmd) error { | ||
c.Stdin, c.Stdout, c.Stderr = i.Slave, i.Slave, i.Slave | ||
if c.SysProcAttr == nil { | ||
c.SysProcAttr = &syscall.SysProcAttr{} | ||
} | ||
c.SysProcAttr.Setctty = true | ||
c.SysProcAttr.Setsid = true | ||
return c.Start() | ||
} | ||
|
||
func newPty(_ Context, _ string, win Window, modes ssh.TerminalModes) (_ impl, rErr error) { | ||
ptm, pts, err := pty.Open() | ||
if err != nil { | ||
return impl{}, err | ||
} | ||
|
||
conn, err := ptm.SyscallConn() | ||
if err != nil { | ||
return impl{}, err | ||
} | ||
|
||
if err := conn.Control(func(fd uintptr) { | ||
rErr = applyTerminalModesToFd(fd, win.Width, win.Height, modes) | ||
}); err != nil { | ||
return impl{}, err | ||
} | ||
|
||
return impl{Master: ptm, Slave: pts}, rErr | ||
} | ||
|
||
func applyTerminalModesToFd(fd uintptr, width int, height int, modes ssh.TerminalModes) error { | ||
// Get the current TTY configuration. | ||
tios, err := termios.GTTY(int(fd)) | ||
if err != nil { | ||
return fmt.Errorf("GTTY: %w", err) | ||
} | ||
|
||
// Apply the modes from the SSH request. | ||
tios.Row = height | ||
tios.Col = width | ||
|
||
for c, v := range modes { | ||
if c == ssh.TTY_OP_ISPEED { | ||
tios.Ispeed = int(v) | ||
continue | ||
} | ||
if c == ssh.TTY_OP_OSPEED { | ||
tios.Ospeed = int(v) | ||
continue | ||
} | ||
k, ok := terminalModeFlagNames[c] | ||
if !ok { | ||
continue | ||
} | ||
if _, ok := tios.CC[k]; ok { | ||
tios.CC[k] = uint8(v) | ||
continue | ||
} | ||
if _, ok := tios.Opts[k]; ok { | ||
tios.Opts[k] = v > 0 | ||
continue | ||
} | ||
} | ||
|
||
// Save the new TTY configuration. | ||
if _, err := tios.STTY(int(fd)); err != nil { | ||
return fmt.Errorf("STTY: %w", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// terminalModeFlagNames maps the SSH terminal mode flags to mnemonic | ||
// names used by the termios package. | ||
var terminalModeFlagNames = map[uint8]string{ | ||
ssh.VINTR: "intr", | ||
ssh.VQUIT: "quit", | ||
ssh.VERASE: "erase", | ||
ssh.VKILL: "kill", | ||
ssh.VEOF: "eof", | ||
ssh.VEOL: "eol", | ||
ssh.VEOL2: "eol2", | ||
ssh.VSTART: "start", | ||
ssh.VSTOP: "stop", | ||
ssh.VSUSP: "susp", | ||
ssh.VDSUSP: "dsusp", | ||
ssh.VREPRINT: "rprnt", | ||
ssh.VWERASE: "werase", | ||
ssh.VLNEXT: "lnext", | ||
ssh.VFLUSH: "flush", | ||
ssh.VSWTCH: "swtch", | ||
ssh.VSTATUS: "status", | ||
ssh.VDISCARD: "discard", | ||
ssh.IGNPAR: "ignpar", | ||
ssh.PARMRK: "parmrk", | ||
ssh.INPCK: "inpck", | ||
ssh.ISTRIP: "istrip", | ||
ssh.INLCR: "inlcr", | ||
ssh.IGNCR: "igncr", | ||
ssh.ICRNL: "icrnl", | ||
ssh.IUCLC: "iuclc", | ||
ssh.IXON: "ixon", | ||
ssh.IXANY: "ixany", | ||
ssh.IXOFF: "ixoff", | ||
ssh.IMAXBEL: "imaxbel", | ||
ssh.IUTF8: "iutf8", | ||
ssh.ISIG: "isig", | ||
ssh.ICANON: "icanon", | ||
ssh.XCASE: "xcase", | ||
ssh.ECHO: "echo", | ||
ssh.ECHOE: "echoe", | ||
ssh.ECHOK: "echok", | ||
ssh.ECHONL: "echonl", | ||
ssh.NOFLSH: "noflsh", | ||
ssh.TOSTOP: "tostop", | ||
ssh.IEXTEN: "iexten", | ||
ssh.ECHOCTL: "echoctl", | ||
ssh.ECHOKE: "echoke", | ||
ssh.PENDIN: "pendin", | ||
ssh.OPOST: "opost", | ||
ssh.OLCUC: "olcuc", | ||
ssh.ONLCR: "onlcr", | ||
ssh.OCRNL: "ocrnl", | ||
ssh.ONOCR: "onocr", | ||
ssh.ONLRET: "onlret", | ||
ssh.CS7: "cs7", | ||
ssh.CS8: "cs8", | ||
ssh.PARENB: "parenb", | ||
ssh.PARODD: "parodd", | ||
ssh.TTY_OP_ISPEED: "tty_op_ispeed", | ||
ssh.TTY_OP_OSPEED: "tty_op_ospeed", | ||
} |
Oops, something went wrong.