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