// +build darwin freebsd linux solaris package console import ( "os" "golang.org/x/sys/unix" ) // NewPty creates a new pty pair // The master is returned as the first console and a string // with the path to the pty slave is returned as the second func NewPty() (Console, string, error) { f, err := os.OpenFile("/dev/ptmx", unix.O_RDWR|unix.O_NOCTTY|unix.O_CLOEXEC, 0) if err != nil { return nil, "", err } slave, err := ptsname(f) if err != nil { return nil, "", err } if err := unlockpt(f); err != nil { return nil, "", err } m, err := newMaster(f) if err != nil { return nil, "", err } return m, slave, nil } type master struct { f *os.File original *unix.Termios } func (m *master) Read(b []byte) (int, error) { return m.f.Read(b) } func (m *master) Write(b []byte) (int, error) { return m.f.Write(b) } func (m *master) Close() error { return m.f.Close() } func (m *master) Resize(ws WinSize) error { return tcswinsz(m.f.Fd(), ws) } func (m *master) ResizeFrom(c Console) error { ws, err := c.Size() if err != nil { return err } return m.Resize(ws) } func (m *master) Reset() error { if m.original == nil { return nil } return tcset(m.f.Fd(), m.original) } func (m *master) getCurrent() (unix.Termios, error) { var termios unix.Termios if err := tcget(m.f.Fd(), &termios); err != nil { return unix.Termios{}, err } return termios, nil } func (m *master) SetRaw() error { rawState, err := m.getCurrent() if err != nil { return err } rawState = cfmakeraw(rawState) rawState.Oflag = rawState.Oflag | unix.OPOST return tcset(m.f.Fd(), &rawState) } func (m *master) DisableEcho() error { rawState, err := m.getCurrent() if err != nil { return err } rawState.Lflag = rawState.Lflag &^ unix.ECHO return tcset(m.f.Fd(), &rawState) } func (m *master) Size() (WinSize, error) { return tcgwinsz(m.f.Fd()) } func (m *master) Fd() uintptr { return m.f.Fd() } func (m *master) Name() string { return m.f.Name() } // checkConsole checks if the provided file is a console func checkConsole(f *os.File) error { var termios unix.Termios if tcget(f.Fd(), &termios) != nil { return ErrNotAConsole } return nil } func newMaster(f *os.File) (Console, error) { m := &master{ f: f, } t, err := m.getCurrent() if err != nil { return nil, err } m.original = &t return m, nil } // ClearONLCR sets the necessary tty_ioctl(4)s to ensure that a pty pair // created by us acts normally. In particular, a not-very-well-known default of // Linux unix98 ptys is that they have +onlcr by default. While this isn't a // problem for terminal emulators, because we relay data from the terminal we // also relay that funky line discipline. func ClearONLCR(fd uintptr) error { return setONLCR(fd, false) } // SetONLCR sets the necessary tty_ioctl(4)s to ensure that a pty pair // created by us acts as intended for a terminal emulator. func SetONLCR(fd uintptr) error { return setONLCR(fd, true) }