github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/utils/prompt/stdin.go (about)

     1  /*
     2  Copyright 2021 Gravitational, Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package prompt
    18  
    19  import (
    20  	"os"
    21  	"sync"
    22  )
    23  
    24  var (
    25  	stdinMU sync.Mutex
    26  	stdin   StdinReader
    27  )
    28  
    29  // StdinReader contains ContextReader methods applicable to stdin.
    30  type StdinReader interface {
    31  	IsTerminal() bool
    32  	Reader
    33  	SecureReader
    34  }
    35  
    36  // Stdin returns a singleton ContextReader wrapped around os.Stdin.
    37  //
    38  // os.Stdin should not be used directly after the first call to this function
    39  // to avoid losing data.
    40  func Stdin() StdinReader {
    41  	stdinMU.Lock()
    42  	defer stdinMU.Unlock()
    43  	if stdin == nil {
    44  		cr := NewContextReader(os.Stdin)
    45  		go cr.handleInterrupt()
    46  		stdin = cr
    47  	}
    48  	return stdin
    49  }
    50  
    51  // SetStdin allows callers to change the Stdin reader.
    52  // Useful to replace Stdin for tests, but should be avoided in production code.
    53  func SetStdin(rd StdinReader) {
    54  	stdinMU.Lock()
    55  	defer stdinMU.Unlock()
    56  	stdin = rd
    57  }
    58  
    59  // NotifyExit notifies prompt singletons, such as Stdin, that the program is
    60  // about to exit. This allows singletons to perform actions such as restoring
    61  // terminal state.
    62  // Once NotifyExit is called the singletons will be closed.
    63  func NotifyExit() {
    64  	// Note: don't call methods such as Stdin() here, we don't want to
    65  	// inadvertently hijack the prompts on exit.
    66  	stdinMU.Lock()
    67  	if cr, ok := stdin.(*ContextReader); ok {
    68  		_ = cr.Close()
    69  	}
    70  	stdinMU.Unlock()
    71  }