Skip to content

Commit

Permalink
Fix Ctrl+key handling by switching terminal to raw mode (closes aws#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
jbbarth committed Sep 16, 2023
1 parent a5ad715 commit 9dc328b
Show file tree
Hide file tree
Showing 18 changed files with 1,913 additions and 0 deletions.
Expand Up @@ -26,6 +26,7 @@ import (

"github.com/aws/session-manager-plugin/src/log"
"github.com/aws/session-manager-plugin/src/message"
"golang.org/x/term"
)

// disableEchoAndInputBuffering disables echo to avoid double echo and disable input buffering
Expand Down Expand Up @@ -67,6 +68,14 @@ func (s *ShellSession) handleKeyboardInput(log log.T) (err error) {
//handle double echo and disable input buffering
s.disableEchoAndInputBuffering()

//switch terminal in raw mode
oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
log.Errorf("Error switching terminal to raw mode: %s", err)
return
}
defer term.Restore(int(os.Stdin.Fd()), oldState)

stdinBytes := make([]byte, StdinBufferLimit)
reader := bufio.NewReader(os.Stdin)
for {
Expand Down
26 changes: 26 additions & 0 deletions vendor/src/golang.org/x/term/CONTRIBUTING.md
@@ -0,0 +1,26 @@
# Contributing to Go

Go is an open source project.

It is the work of hundreds of contributors. We appreciate your help!

## Filing issues

When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:

1. What version of Go are you using (`go version`)?
2. What operating system and processor architecture are you using?
3. What did you do?
4. What did you expect to see?
5. What did you see instead?

General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
The gophers there will answer or ask you to file an issue if you've tripped over a bug.

## Contributing code

Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
before sending patches.

Unless otherwise noted, the Go source files are distributed under
the BSD-style license found in the LICENSE file.
27 changes: 27 additions & 0 deletions vendor/src/golang.org/x/term/LICENSE
@@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22 changes: 22 additions & 0 deletions vendor/src/golang.org/x/term/PATENTS
@@ -0,0 +1,22 @@
Additional IP Rights Grant (Patents)

"This implementation" means the copyrightable works distributed by
Google as part of the Go project.

Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.
19 changes: 19 additions & 0 deletions vendor/src/golang.org/x/term/README.md
@@ -0,0 +1,19 @@
# Go terminal/console support

[![Go Reference](https://pkg.go.dev/badge/golang.org/x/term.svg)](https://pkg.go.dev/golang.org/x/term)

This repository provides Go terminal and console support packages.

## Download/Install

The easiest way to install is to run `go get -u golang.org/x/term`. You can
also manually git clone the repository to `$GOPATH/src/golang.org/x/term`.

## Report Issues / Send Patches

This repository uses Gerrit for code changes. To learn how to submit changes to
this repository, see https://golang.org/doc/contribute.html.

The main issue tracker for the term repository is located at
https://github.com/golang/go/issues. Prefix your issue with "x/term:" in the
subject line, so it is easy to find.
1 change: 1 addition & 0 deletions vendor/src/golang.org/x/term/codereview.cfg
@@ -0,0 +1 @@
issuerepo: golang/go
5 changes: 5 additions & 0 deletions vendor/src/golang.org/x/term/go.mod
@@ -0,0 +1,5 @@
module golang.org/x/term

go 1.17

require golang.org/x/sys v0.12.0
2 changes: 2 additions & 0 deletions vendor/src/golang.org/x/term/go.sum
@@ -0,0 +1,2 @@
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
60 changes: 60 additions & 0 deletions vendor/src/golang.org/x/term/term.go
@@ -0,0 +1,60 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package term provides support functions for dealing with terminals, as
// commonly found on UNIX systems.
//
// Putting a terminal into raw mode is the most common requirement:
//
// oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
// if err != nil {
// panic(err)
// }
// defer term.Restore(int(os.Stdin.Fd()), oldState)
//
// Note that on non-Unix systems os.Stdin.Fd() may not be 0.
package term

// State contains the state of a terminal.
type State struct {
state
}

// IsTerminal returns whether the given file descriptor is a terminal.
func IsTerminal(fd int) bool {
return isTerminal(fd)
}

// MakeRaw puts the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd int) (*State, error) {
return makeRaw(fd)
}

// GetState returns the current state of a terminal which may be useful to
// restore the terminal after a signal.
func GetState(fd int) (*State, error) {
return getState(fd)
}

// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func Restore(fd int, oldState *State) error {
return restore(fd, oldState)
}

// GetSize returns the visible dimensions of the given terminal.
//
// These dimensions don't include any scrollback buffer height.
func GetSize(fd int) (width, height int, err error) {
return getSize(fd)
}

// ReadPassword reads a line of input from a terminal without local echo. This
// is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n.
func ReadPassword(fd int) ([]byte, error) {
return readPassword(fd)
}
42 changes: 42 additions & 0 deletions vendor/src/golang.org/x/term/term_plan9.go
@@ -0,0 +1,42 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package term

import (
"fmt"
"runtime"

"golang.org/x/sys/plan9"
)

type state struct{}

func isTerminal(fd int) bool {
path, err := plan9.Fd2path(fd)
if err != nil {
return false
}
return path == "/dev/cons" || path == "/mnt/term/dev/cons"
}

func makeRaw(fd int) (*State, error) {
return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}

func getState(fd int) (*State, error) {
return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}

func restore(fd int, state *State) error {
return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}

func getSize(fd int) (width, height int, err error) {
return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}

func readPassword(fd int) ([]byte, error) {
return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}
41 changes: 41 additions & 0 deletions vendor/src/golang.org/x/term/term_test.go
@@ -0,0 +1,41 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package term_test

import (
"os"
"runtime"
"testing"

"golang.org/x/term"
)

func TestIsTerminalTempFile(t *testing.T) {
file, err := os.CreateTemp("", "TestIsTerminalTempFile")
if err != nil {
t.Fatal(err)
}
defer os.Remove(file.Name())
defer file.Close()

if term.IsTerminal(int(file.Fd())) {
t.Fatalf("IsTerminal unexpectedly returned true for temporary file %s", file.Name())
}
}

func TestIsTerminalTerm(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skipf("unknown terminal path for GOOS %v", runtime.GOOS)
}
file, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
if err != nil {
t.Fatal(err)
}
defer file.Close()

if !term.IsTerminal(int(file.Fd())) {
t.Fatalf("IsTerminal unexpectedly returned false for terminal file %s", file.Name())
}
}
92 changes: 92 additions & 0 deletions vendor/src/golang.org/x/term/term_unix.go
@@ -0,0 +1,92 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos

package term

import (
"golang.org/x/sys/unix"
)

type state struct {
termios unix.Termios
}

func isTerminal(fd int) bool {
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
return err == nil
}

func makeRaw(fd int) (*State, error) {
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
if err != nil {
return nil, err
}

oldState := State{state{termios: *termios}}

// This attempts to replicate the behaviour documented for cfmakeraw in
// the termios(3) manpage.
termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
termios.Oflag &^= unix.OPOST
termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
termios.Cflag &^= unix.CSIZE | unix.PARENB
termios.Cflag |= unix.CS8
termios.Cc[unix.VMIN] = 1
termios.Cc[unix.VTIME] = 0
if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, termios); err != nil {
return nil, err
}

return &oldState, nil
}

func getState(fd int) (*State, error) {
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
if err != nil {
return nil, err
}

return &State{state{termios: *termios}}, nil
}

func restore(fd int, state *State) error {
return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios)
}

func getSize(fd int) (width, height int, err error) {
ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
if err != nil {
return 0, 0, err
}
return int(ws.Col), int(ws.Row), nil
}

// passwordReader is an io.Reader that reads from a specific file descriptor.
type passwordReader int

func (r passwordReader) Read(buf []byte) (int, error) {
return unix.Read(int(r), buf)
}

func readPassword(fd int) ([]byte, error) {
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
if err != nil {
return nil, err
}

newState := *termios
newState.Lflag &^= unix.ECHO
newState.Lflag |= unix.ICANON | unix.ISIG
newState.Iflag |= unix.ICRNL
if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newState); err != nil {
return nil, err
}

defer unix.IoctlSetTermios(fd, ioctlWriteTermios, termios)

return readPasswordLine(passwordReader(fd))
}
13 changes: 13 additions & 0 deletions vendor/src/golang.org/x/term/term_unix_bsd.go
@@ -0,0 +1,13 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build darwin || dragonfly || freebsd || netbsd || openbsd
// +build darwin dragonfly freebsd netbsd openbsd

package term

import "golang.org/x/sys/unix"

const ioctlReadTermios = unix.TIOCGETA
const ioctlWriteTermios = unix.TIOCSETA
13 changes: 13 additions & 0 deletions vendor/src/golang.org/x/term/term_unix_other.go
@@ -0,0 +1,13 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build aix || linux || solaris || zos
// +build aix linux solaris zos

package term

import "golang.org/x/sys/unix"

const ioctlReadTermios = unix.TCGETS
const ioctlWriteTermios = unix.TCSETS

0 comments on commit 9dc328b

Please sign in to comment.