// +build windows // Note the terminal manipulation functions herein are mostly stubs. They // don't really do anything and the xs demo client depends on a wrapper // script using the 'stty' tool to actually set the proper mode for // password login and raw mode required, then restoring it upon logout/exit. // // mintty uses named pipes and ptys rather than Windows 'console' // mode, and Go's x/crypto/ssh/terminal libs only work for the latter, so // until some truly cross-platform terminal mode handling makes it into the // go std lib I'm not going to jump through hoops trying to be cross-platform // here; the wrapper does the bare minimum to make the client workable // under MSYS+mintty which is what I use. package xs import ( "io" "os/exec" "golang.org/x/sys/windows" ) type State struct { } // MakeRaw put 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 uintptr) (*State, error) { // This doesn't really work. The exec.Command() runs a sub-shell // so the stty mods don't affect the client process. cmd := exec.Command("stty", "-echo raw") cmd.Run() return &State{}, nil } // GetState returns the current state of a terminal which may be useful to // restore the terminal after a signal. func GetState(fd uintptr) (*State, error) { return &State{}, nil } // Restore restores the terminal connected to the given file descriptor to a // previous state. func Restore(fd uintptr, state *State) error { cmd := exec.Command("stty", "echo cooked") cmd.Run() return nil } // 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 uintptr) ([]byte, error) { return readPasswordLine(passwordReader(fd)) } // passwordReader is an io.Reader that reads from a specific file descriptor. type passwordReader windows.Handle func (r passwordReader) Read(buf []byte) (int, error) { return windows.Read(windows.Handle(r), buf) } // readPasswordLine reads from reader until it finds \n or io.EOF. // The slice returned does not include the \n. // readPasswordLine also ignores any \r it finds. func readPasswordLine(reader io.Reader) ([]byte, error) { var buf [1]byte var ret []byte for { n, err := reader.Read(buf[:]) if n > 0 { switch buf[0] { case '\n': return ret, nil case '\r': // remove \r from passwords on Windows default: ret = append(ret, buf[0]) } continue } if err != nil { if err == io.EOF && len(ret) > 0 { return ret, nil } return ret, err } } }