Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ Latest
================
- Add SSH-style escape sequences to shell sessions (~. to terminate, ~? for help)
- Fix port forwarding sessions silently dying after WebSocket reconnection
- Fix shell sessions terminating on macOS/BSD when Ctrl+Y is pressed by disabling the DSUSP terminal control character

1.2.814.0
================
Expand Down
32 changes: 32 additions & 0 deletions src/sessionmanagerplugin/session/shellsession/shellsession_bsd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may not
// use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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.

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

// Package shellsession starts shell session.
package shellsession

import "bytes"

// disableDelayedSuspend disables the DSUSP terminal special character (Ctrl+Y by
// default) on BSD-derived systems, including macOS.
//
// In cbreak mode the terminal driver still acts on DSUSP, so pressing Ctrl+Y
// triggers a delayed suspend that causes the Stdin read to fail with
// "read /dev/stdin: resource temporarily unavailable" and the session to
// terminate. Linux does not implement DSUSP, so this is a no-op there.
// See https://github.com/aws/session-manager-plugin/issues/29.
func (s *ShellSession) disableDelayedSuspend() {
setState(bytes.NewBufferString("dsusp undef"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may not
// use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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.

//go:build linux
// +build linux

// Package shellsession starts shell session.
package shellsession

// disableDelayedSuspend is a no-op on Linux, which does not implement the DSUSP
// (delayed suspend) terminal special character. The "dsusp" setting is rejected
// by stty on Linux, so it must only be applied on BSD-derived systems. See the
// BSD implementation and https://github.com/aws/session-manager-plugin/issues/29.
func (s *ShellSession) disableDelayedSuspend() {}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"errors"
"os"
"os/exec"
"strings"
"time"

"github.com/aws/session-manager-plugin/src/log"
Expand All @@ -34,6 +35,7 @@ func (s *ShellSession) disableEchoAndInputBuffering() {
getState(&s.originalSttyState)
setState(bytes.NewBufferString("cbreak"))
setState(bytes.NewBufferString("-echo"))
s.disableDelayedSuspend()
}

// getState gets current state of terminal
Expand All @@ -44,9 +46,12 @@ func getState(state *bytes.Buffer) error {
return cmd.Run()
}

// setState sets the new settings to terminal
// setState sets the new settings to terminal. The buffer may contain several
// whitespace-separated stty operands (for example "dsusp undef"), which must be
// passed to stty as individual arguments rather than a single operand.
func setState(state *bytes.Buffer) error {
cmd := exec.Command("stty", state.String())
args := strings.Fields(state.String())
cmd := exec.Command("stty", args...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
return cmd.Run()
Expand Down