From c4672c3a84f944113a124e5c1d401ff7ce932675 Mon Sep 17 00:00:00 2001 From: Neil Johnson Date: Fri, 8 Jan 2021 15:44:59 -0500 Subject: [PATCH 1/2] Add support for z/OS Add an implementation of NewPty for the zos platform. The other functions in console_zos.go exactly match those in console_unix.go. There is no need for ptsname/unlockpt on z/OS, so tc_zos.go looks just like the implementation for linux/solaris except for the omission of those functions. Signed-off-by: Neil Johnson --- console_zos.go | 163 +++++++++++++++++++++++++++++++++++++++++++++++++ tc_unix.go | 2 +- tc_zos.go | 26 ++++++++ 3 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 console_zos.go create mode 100644 tc_zos.go diff --git a/console_zos.go b/console_zos.go new file mode 100644 index 0000000..b348a83 --- /dev/null +++ b/console_zos.go @@ -0,0 +1,163 @@ +// +build zos + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package console + +import ( + "fmt" + "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) { + var f File + var err error + var slave string + for i := 0;; i++ { + ptyp := fmt.Sprintf("/dev/ptyp%04d", i) + f, err = os.OpenFile(ptyp, os.O_RDWR, 0600) + if err == nil { + slave = fmt.Sprintf("/dev/ttyp%04d", i) + break + } + if os.IsNotExist(err) { + return nil, "", err + } + // else probably Resource Busy + } + m, err := newMaster(f) + if err != nil { + return nil, "", err + } + return m, slave, nil +} + +type master struct { + f 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 File) error { + var termios unix.Termios + if tcget(f.Fd(), &termios) != nil { + return ErrNotAConsole + } + return nil +} + +func newMaster(f 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) +} diff --git a/tc_unix.go b/tc_unix.go index 5cd4c55..a6bf01e 100644 --- a/tc_unix.go +++ b/tc_unix.go @@ -1,4 +1,4 @@ -// +build darwin freebsd linux netbsd openbsd solaris +// +build darwin freebsd linux netbsd openbsd solaris zos /* Copyright The containerd Authors. diff --git a/tc_zos.go b/tc_zos.go new file mode 100644 index 0000000..4262eaf --- /dev/null +++ b/tc_zos.go @@ -0,0 +1,26 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package console + +import ( + "golang.org/x/sys/unix" +) + +const ( + cmdTcGet = unix.TCGETS + cmdTcSet = unix.TCSETS +) From 70974496a6a52dc828ed9eeaaf451485a7598d53 Mon Sep 17 00:00:00 2001 From: Neil Johnson Date: Fri, 8 Jan 2021 16:12:28 -0500 Subject: [PATCH 2/2] Console test on z/OS The echo -n switch is not always available on z/OS, so 'echo -n' has been changed to 'printf' so that the same test pattern can be reliably produced across platforms. Same goes for the seq program, so changed the sh loop to a go loop. Signed-off-by: Neil Johnson --- console_test.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/console_test.go b/console_test.go index c6a4113..59ef35c 100644 --- a/console_test.go +++ b/console_test.go @@ -1,4 +1,4 @@ -// +build linux solaris freebsd +// +build linux solaris zos freebsd /* Copyright The containerd Authors. @@ -20,7 +20,6 @@ package console import ( "bytes" - "fmt" "io" "os" "os/exec" @@ -69,11 +68,6 @@ func TestConsolePty(t *testing.T) { iteration := 10 - cmd := exec.Command("sh", "-c", fmt.Sprintf("for x in `seq 1 %d`; do echo -n test; done", iteration)) - cmd.Stdin = slave - cmd.Stdout = slave - cmd.Stderr = slave - var ( b bytes.Buffer wg sync.WaitGroup @@ -84,8 +78,15 @@ func TestConsolePty(t *testing.T) { wg.Done() }() - if err := cmd.Run(); err != nil { - t.Fatal(err) + for i := 0; i < iteration; i++ { + cmd := exec.Command("sh", "-c", "printf test") + cmd.Stdin = slave + cmd.Stdout = slave + cmd.Stderr = slave + + if err := cmd.Run(); err != nil { + t.Fatal(err) + } } slave.Close() wg.Wait()