1// +build darwin freebsd linux openbsd solaris dragonfly 2 3/* 4 Copyright The containerd Authors. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17*/ 18 19package console 20 21import ( 22 "os" 23 24 "golang.org/x/sys/unix" 25) 26 27// NewPty creates a new pty pair 28// The master is returned as the first console and a string 29// with the path to the pty slave is returned as the second 30func NewPty() (Console, string, error) { 31 f, err := os.OpenFile("/dev/ptmx", unix.O_RDWR|unix.O_NOCTTY|unix.O_CLOEXEC, 0) 32 if err != nil { 33 return nil, "", err 34 } 35 slave, err := ptsname(f) 36 if err != nil { 37 return nil, "", err 38 } 39 if err := unlockpt(f); err != nil { 40 return nil, "", err 41 } 42 m, err := newMaster(f) 43 if err != nil { 44 return nil, "", err 45 } 46 return m, slave, nil 47} 48 49type master struct { 50 f *os.File 51 original *unix.Termios 52} 53 54func (m *master) Read(b []byte) (int, error) { 55 return m.f.Read(b) 56} 57 58func (m *master) Write(b []byte) (int, error) { 59 return m.f.Write(b) 60} 61 62func (m *master) Close() error { 63 return m.f.Close() 64} 65 66func (m *master) Resize(ws WinSize) error { 67 return tcswinsz(m.f.Fd(), ws) 68} 69 70func (m *master) ResizeFrom(c Console) error { 71 ws, err := c.Size() 72 if err != nil { 73 return err 74 } 75 return m.Resize(ws) 76} 77 78func (m *master) Reset() error { 79 if m.original == nil { 80 return nil 81 } 82 return tcset(m.f.Fd(), m.original) 83} 84 85func (m *master) getCurrent() (unix.Termios, error) { 86 var termios unix.Termios 87 if err := tcget(m.f.Fd(), &termios); err != nil { 88 return unix.Termios{}, err 89 } 90 return termios, nil 91} 92 93func (m *master) SetRaw() error { 94 rawState, err := m.getCurrent() 95 if err != nil { 96 return err 97 } 98 rawState = cfmakeraw(rawState) 99 rawState.Oflag = rawState.Oflag | unix.OPOST 100 return tcset(m.f.Fd(), &rawState) 101} 102 103func (m *master) DisableEcho() error { 104 rawState, err := m.getCurrent() 105 if err != nil { 106 return err 107 } 108 rawState.Lflag = rawState.Lflag &^ unix.ECHO 109 return tcset(m.f.Fd(), &rawState) 110} 111 112func (m *master) Size() (WinSize, error) { 113 return tcgwinsz(m.f.Fd()) 114} 115 116func (m *master) Fd() uintptr { 117 return m.f.Fd() 118} 119 120func (m *master) Name() string { 121 return m.f.Name() 122} 123 124// checkConsole checks if the provided file is a console 125func checkConsole(f *os.File) error { 126 var termios unix.Termios 127 if tcget(f.Fd(), &termios) != nil { 128 return ErrNotAConsole 129 } 130 return nil 131} 132 133func newMaster(f *os.File) (Console, error) { 134 m := &master{ 135 f: f, 136 } 137 t, err := m.getCurrent() 138 if err != nil { 139 return nil, err 140 } 141 m.original = &t 142 return m, nil 143} 144 145// ClearONLCR sets the necessary tty_ioctl(4)s to ensure that a pty pair 146// created by us acts normally. In particular, a not-very-well-known default of 147// Linux unix98 ptys is that they have +onlcr by default. While this isn't a 148// problem for terminal emulators, because we relay data from the terminal we 149// also relay that funky line discipline. 150func ClearONLCR(fd uintptr) error { 151 return setONLCR(fd, false) 152} 153 154// SetONLCR sets the necessary tty_ioctl(4)s to ensure that a pty pair 155// created by us acts as intended for a terminal emulator. 156func SetONLCR(fd uintptr) error { 157 return setONLCR(fd, true) 158} 159