v0.33.0 tui tap for live view and console enhancements
This commit is contained in:
1 parent
a1e9d21d06
commit
87f5576ccf
41 files changed
+4606
-449
No files matched your search
@@ -298,9 +298,26 @@ func readDevice(path, user string, generate bool) ([]byte, bool, error) {
|
||||
return data, false, err
|
||||
}
|
||||
|
||||
// asker answers in the terminal's place while a host's screen owns it
|
||||
var asker atomic.Pointer[func(user string, generate bool) ([]byte, error)]
|
||||
|
||||
// SetAsker has fn answer what the terminal would be asked; nil asks the
|
||||
// terminal again. With generate, fn draws a password for an empty answer and
|
||||
// shows it once, never in a log.
|
||||
func SetAsker(fn func(user string, generate bool) ([]byte, error)) {
|
||||
if fn == nil {
|
||||
asker.Store(nil)
|
||||
return
|
||||
}
|
||||
asker.Store(&fn)
|
||||
}
|
||||
|
||||
// ask prompts on the terminal at path without echo. A generated password is
|
||||
// shown there only: logs persist, and stderr is often a file.
|
||||
func ask(path, user string, generate bool) ([]byte, error) {
|
||||
if fn := asker.Load(); fn != nil {
|
||||
return (*fn)(user, generate)
|
||||
}
|
||||
tty, err := os.OpenFile(path, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/lixenwraith/logwisp/internal/testutil"
|
||||
|
||||
"github.com/lixenwraith/auth"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
@@ -689,6 +690,43 @@ func TestOnceReadSourcesAreReadAtStartupOnly(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// While a host's screen owns the terminal its asker answers in the prompt's
|
||||
// place, once per user, like the terminal (a pty master is a terminal).
|
||||
func TestAskerAnswersForTheTerminal(t *testing.T) {
|
||||
probe, err := os.Open("/dev/ptmx")
|
||||
if err != nil {
|
||||
t.Skip("no pseudo-terminal:", err)
|
||||
}
|
||||
isTerminal := term.IsTerminal(int(probe.Fd()))
|
||||
probe.Close()
|
||||
if !isTerminal {
|
||||
t.Skip("/dev/ptmx is no terminal here")
|
||||
}
|
||||
var asked []string
|
||||
SetAsker(func(user string, generate bool) ([]byte, error) {
|
||||
asked = append(asked, fmt.Sprintf("%s %v", user, generate))
|
||||
return []byte("screen-secret"), nil
|
||||
})
|
||||
t.Cleanup(func() { SetAsker(nil); readOnce.Lock(); clear(readOnce.m); readOnce.Unlock() })
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
for _, user := range []string{"edge", "edge", "aggregator"} {
|
||||
if pw, err := ReadPassword("/dev/ptmx", user, true); err != nil || pw != "screen-secret" {
|
||||
t.Errorf("%s: %q, %v", user, pw, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("the terminal was asked, not the asker")
|
||||
}
|
||||
if fmt.Sprint(asked) != "[edge true aggregator true]" {
|
||||
t.Errorf("asked %v", asked)
|
||||
}
|
||||
}
|
||||
|
||||
// A device is classed by the file it opens, not by stat: FreeBSD stats
|
||||
// /dev/stdin as a device whatever it holds, so one that is no terminal is read.
|
||||
func TestADeviceThatIsNoTerminalIsRead(t *testing.T) {
|
||||
|
||||
Reference in new issue
Block a user