diff --git a/.ci/faketty.sh b/.ci/faketty.sh index 6e51510e..e20b75a1 100755 --- a/.ci/faketty.sh +++ b/.ci/faketty.sh @@ -11,7 +11,8 @@ source "${cidir}/lib.sh" # function to run unit test always with a tty function faketty() { - script -qfec $@; + if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then script -qfec $@; fi + if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then $@; fi } faketty $@ diff --git a/.travis.yml b/.travis.yml index 75653716..cd5d36cf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,16 @@ # SPDX-License-Identifier: Apache-2.0 # -sudo: required -dist: trusty + +os: + - linux + - osx + +matrix: + include: + - os: linux + sudo: required + dist: trusty language: go go_import_path: github.com/kata-containers/shim @@ -17,8 +25,8 @@ before_script: - ".ci/static-checks.sh" before_install: - - sudo apt-get update -qq - - sudo apt-get install -y -qq automake + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get update -qq ; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install -y -qq automake ; fi install: - cd ${TRAVIS_BUILD_DIR} && make diff --git a/signals.go b/signals.go index c28239c9..ba87eb5e 100644 --- a/signals.go +++ b/signals.go @@ -19,15 +19,14 @@ import ( // // The value is true if receiving the signal should be fatal. var handledSignalsMap = map[syscall.Signal]bool{ - syscall.SIGABRT: true, - syscall.SIGBUS: true, - syscall.SIGILL: true, - syscall.SIGQUIT: true, - syscall.SIGSEGV: true, - syscall.SIGSTKFLT: true, - syscall.SIGSYS: true, - syscall.SIGTRAP: true, - syscall.SIGUSR1: false, + syscall.SIGABRT: true, + syscall.SIGBUS: true, + syscall.SIGILL: true, + syscall.SIGQUIT: true, + syscall.SIGSEGV: true, + syscall.SIGSYS: true, + syscall.SIGTRAP: true, + syscall.SIGUSR1: false, } func handlePanic() { diff --git a/terminal_darwin.go b/terminal_darwin.go new file mode 100644 index 00000000..d6d8a94b --- /dev/null +++ b/terminal_darwin.go @@ -0,0 +1,49 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "golang.org/x/sys/unix" +) + +const ( + termiosIFlagRawTermInvMask = (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON) + termiosOFlagRawTermInvMask = unix.OPOST + termiosLFlagRawTermInvMask = (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN) + termiosCFlagRawTermInvMask = unix.PARENB + termiosCFlagRawTermMask = unix.CS8 + termiosCcVMinRawTermVal = 1 + termiosCcVTimeRawTermVal = 0 +) + +func setupTerminal(fd int) (*unix.Termios, error) { + termios, err := unix.IoctlGetTermios(fd, unix.TIOCGETA) + if err != nil { + return nil, err + } + + var savedTermios unix.Termios + savedTermios = *termios + + // Set the terminal in raw mode + termios.Iflag &^= termiosIFlagRawTermInvMask + termios.Oflag &^= termiosOFlagRawTermInvMask + termios.Lflag &^= termiosLFlagRawTermInvMask + termios.Cflag &^= termiosCFlagRawTermInvMask + termios.Cflag |= termiosCFlagRawTermMask + termios.Cc[unix.VMIN] = termiosCcVMinRawTermVal + termios.Cc[unix.VTIME] = termiosCcVTimeRawTermVal + + if err := unix.IoctlSetTermios(fd, unix.TIOCSETA, termios); err != nil { + return nil, err + } + + return &savedTermios, nil +} + +func restoreTerminal(fd int, termios *unix.Termios) error { + return unix.IoctlSetTermios(fd, unix.TIOCSETA, termios) +} diff --git a/terminal_darwin_test.go b/terminal_darwin_test.go new file mode 100644 index 00000000..a7d534ca --- /dev/null +++ b/terminal_darwin_test.go @@ -0,0 +1,72 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "io/ioutil" + "os" + "syscall" + "testing" + + "github.com/stretchr/testify/assert" + "golang.org/x/sys/unix" +) + +const ( + RawModeErr = "should be properly set in raw mode" + FlagRawModeErr = "flag " + RawModeErr + ValueRawModeErr = "value " + RawModeErr +) + +func newTestTerminal(t *testing.T) (*os.File, error) { + if os.Getuid() != 0 { + t.Skip("Skipping this test: Requires to be root") + return nil, nil + } + + return os.OpenFile("/dev/tty", os.O_RDWR, os.ModeDevice) +} + +func TestSetupTerminalOnNonTerminalFailure(t *testing.T) { + file, err := ioutil.TempFile("", "tmp") + assert.Nil(t, err, "Failed to create temporary file") + defer file.Close() + + _, err = setupTerminal(int(file.Fd())) + assert.NotNil(t, err, "Should fail because the file is not a terminal") +} + +func TestSetupTerminalSuccess(t *testing.T) { + file, err := newTestTerminal(t) + + if perr, ok := err.(*os.PathError); ok { + switch perr.Err.(syscall.Errno) { + case syscall.ENXIO: + t.Skip("Skipping this test: Failed to open tty, make sure test is running in a tty") + default: + t.Fatalf("could not open tty %s", err) + } + } + + assert.Nil(t, err, "Failed to create terminal") + defer file.Close() + + savedTermios, err := setupTerminal(int(file.Fd())) + assert.Nil(t, err, "Should not fail because the file is a terminal") + + termios, err := unix.IoctlGetTermios(int(file.Fd()), unix.TIOCGETA) + assert.Nil(t, err, "Failed to get terminal information") + assert.True(t, (termios.Iflag&termiosIFlagRawTermInvMask) == 0, "Termios I %s", FlagRawModeErr) + assert.True(t, (termios.Oflag&termiosOFlagRawTermInvMask) == 0, "Termios O %s", FlagRawModeErr) + assert.True(t, (termios.Lflag&termiosLFlagRawTermInvMask) == 0, "Termios L %s", FlagRawModeErr) + assert.True(t, (termios.Cflag&termiosCFlagRawTermInvMask) == 0, "Termios C %s", FlagRawModeErr) + assert.True(t, (termios.Cflag&termiosCFlagRawTermMask) == termiosCFlagRawTermMask, "Termios C %s", FlagRawModeErr) + assert.True(t, termios.Cc[unix.VMIN] == termiosCcVMinRawTermVal, "Termios CC VMIN %s", ValueRawModeErr) + assert.True(t, termios.Cc[unix.VTIME] == termiosCcVTimeRawTermVal, "Termios CC VTIME %s", ValueRawModeErr) + + err = restoreTerminal(int(file.Fd()), savedTermios) + assert.Nil(t, err, "Terminal should be properly restored") +}