ptyshell

package
v0.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 27, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package ptyshell runs a command attached to a pseudo-terminal.

It exists for one reason: an interactive session needs the *hub* (or the edge agent) to hold a real terminal, not merely to ask for one. `docker exec -t` allocates a pty inside the container and is happy with a pipe on its own stdin — but only while nothing is being typed. Add `-i` and the Docker CLI refuses outright with "the input device is not a TTY", because it is about to put the terminal into raw mode and there is no terminal. Kubernetes and the edge agent have the same shape from the other side: they will happily deliver keystrokes, and a shell reading from a pipe echoes nothing, draws no prompt, and runs no line editor.

So a writable session needs a pty on this side of the transport. That is all this package does — open one, start a command on its slave, hand back the master — and it deliberately does not know what a sandbox is.

Portability

The implementation is Linux-only, which is where cloop's container and Kubernetes executors run and where its agent is installed. Other platforms get Supported() == false and Start returns ErrUnsupported, which callers turn into a pipe-backed session rather than a failure: a terminal without line editing still runs commands and still reads back output, and degrading is better than refusing to let an operator into a sandbox at all.

Index

Constants

This section is empty.

Variables

View Source
var ErrUnsupported = errors.New("ptyshell: pseudo-terminal not supported on this platform")

ErrUnsupported means this platform has no pty implementation here. Callers should fall back to pipes rather than fail.

Functions

func Supported

func Supported() bool

Supported reports whether this platform can allocate a pty here.

Types

type Session

type Session struct {
	// Master is the pty master. Close it to hang up the terminal.
	Master *os.File
	// Cmd is the running command, for Process.Kill. Reap it with Session.Wait,
	// never Cmd.Wait: Close reaps in the background, and exec.Cmd.Wait may run
	// only once — a second call races the first on the Cmd's own fields.
	Cmd *exec.Cmd
	// contains filtered or unexported fields
}

Session is a command running on a pty. Master is both ends of the terminal: reading it yields the command's output, writing it delivers keystrokes.

func Start

func Start(cmd *exec.Cmd, rows, cols uint16) (*Session, error)

Start runs cmd attached to a new pty of the given size.

On success cmd is running, its standard streams are the pty slave, and the slave has been closed in this process so that reading Master returns EOF when the command exits rather than blocking forever on an fd nobody will write.

func (*Session) Close

func (s *Session) Close() error

Close hangs up the terminal and kills the command if it is still running.

Both, in that order, and neither alone is enough. Closing the master sends SIGHUP to the foreground process group, which a shell honours and `sleep` does not; killing without closing leaves the master fd open in this process for as long as the caller holds the Session. A session that outlived its operator would be an unattributed process inside the sandbox still holding the project's credentials, which is the whole reason attach is audited.

func (*Session) Resize

func (s *Session) Resize(rows, cols uint16) error

Resize reports new terminal geometry to the command.

A zero row or column count is ignored rather than applied: a browser tab that has not laid out yet reports 0x0, and a shell told its terminal is zero-by-zero wraps every line at column one.

func (*Session) Wait added in v0.0.3

func (s *Session) Wait() error

Wait waits for the command to exit and returns what Cmd.Wait reported.

Any number of callers may ask, from any goroutine and alongside Close: the command is reaped exactly once and every caller gets that one result. That is what makes it safe for an owner that wants the exit code to wait while Close, which must reap so an abandoned session leaves no zombie, does the same. Both calling Cmd.Wait was a data race inside os/exec, reported under -race every time an operator closed a terminal whose command was exiting anyway.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL