123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- /*
- Copyright 2016 The Kubernetes 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 term
- import (
- "io"
- "os"
- "github.com/docker/docker/pkg/term"
- "k8s.io/kubernetes/pkg/kubectl/util/interrupt"
- )
- // SafeFunc is a function to be invoked by TTY.
- type SafeFunc func() error
- // TTY helps invoke a function and preserve the state of the terminal, even if the process is
- // terminated during execution. It also provides support for terminal resizing for remote command
- // execution/attachment.
- type TTY struct {
- // In is a reader representing stdin. It is a required field.
- In io.Reader
- // Out is a writer representing stdout. It must be set to support terminal resizing. It is an
- // optional field.
- Out io.Writer
- // Raw is true if the terminal should be set raw.
- Raw bool
- // TryDev indicates the TTY should try to open /dev/tty if the provided input
- // is not a file descriptor.
- TryDev bool
- // Parent is an optional interrupt handler provided to this function - if provided
- // it will be invoked after the terminal state is restored. If it is not provided,
- // a signal received during the TTY will result in os.Exit(0) being invoked.
- Parent *interrupt.Handler
- // sizeQueue is set after a call to MonitorSize() and is used to monitor SIGWINCH signals when the
- // user's terminal resizes.
- sizeQueue *sizeQueue
- }
- // IsTerminalIn returns true if t.In is a terminal. Does not check /dev/tty
- // even if TryDev is set.
- func (t TTY) IsTerminalIn() bool {
- return IsTerminal(t.In)
- }
- // IsTerminalOut returns true if t.Out is a terminal. Does not check /dev/tty
- // even if TryDev is set.
- func (t TTY) IsTerminalOut() bool {
- return IsTerminal(t.Out)
- }
- // IsTerminal returns whether the passed object is a terminal or not
- func IsTerminal(i interface{}) bool {
- _, terminal := term.GetFdInfo(i)
- return terminal
- }
- // Safe invokes the provided function and will attempt to ensure that when the
- // function returns (or a termination signal is sent) that the terminal state
- // is reset to the condition it was in prior to the function being invoked. If
- // t.Raw is true the terminal will be put into raw mode prior to calling the function.
- // If the input file descriptor is not a TTY and TryDev is true, the /dev/tty file
- // will be opened (if available).
- func (t TTY) Safe(fn SafeFunc) error {
- inFd, isTerminal := term.GetFdInfo(t.In)
- if !isTerminal && t.TryDev {
- if f, err := os.Open("/dev/tty"); err == nil {
- defer f.Close()
- inFd = f.Fd()
- isTerminal = term.IsTerminal(inFd)
- }
- }
- if !isTerminal {
- return fn()
- }
- var state *term.State
- var err error
- if t.Raw {
- state, err = term.MakeRaw(inFd)
- } else {
- state, err = term.SaveState(inFd)
- }
- if err != nil {
- return err
- }
- return interrupt.Chain(t.Parent, func() {
- if t.sizeQueue != nil {
- t.sizeQueue.stop()
- }
- term.RestoreTerminal(inFd, state)
- }).Run(fn)
- }
|