repos / pico

pico services mono repo
git clone https://github.com/picosh/pico.git

commit
b10aef1
parent
1f6cc61
author
Eric Bower
date
2026-02-25 22:13:25 -0500 EST
fix(pssh): normalize line ending with pty
1 files changed,  +27, -0
M pkg/pssh/server.go
+27, -0
 1@@ -1,6 +1,7 @@
 2 package pssh
 3 
 4 import (
 5+	"bytes"
 6 	"context"
 7 	"crypto/ed25519"
 8 	"crypto/rand"
 9@@ -185,6 +186,32 @@ func (s *SSHServerConnSession) Pty() (*Pty, <-chan Window, bool) {
10 	return s.pty, s.winch, true
11 }
12 
13+// Write overrides the embedded Channel's Write to normalize line endings when PTY is allocated.
14+func (s *SSHServerConnSession) Write(p []byte) (n int, err error) {
15+	s.mu.Lock()
16+	hasPty := s.pty != nil
17+	s.mu.Unlock()
18+
19+	if !hasPty {
20+		// No PTY, write as-is
21+		return s.Channel.Write(p)
22+	}
23+
24+	// When PTY is active, normalize line endings like a real terminal would.
25+	// Replace \n with \r\n, but avoid double \r\n.
26+	normalized := bytes.ReplaceAll(p, []byte{'\n'}, []byte{'\r', '\n'})
27+	normalized = bytes.ReplaceAll(normalized, []byte{'\r', '\r', '\n'}, []byte{'\r', '\n'})
28+
29+	// Write the normalized data
30+	written, err := s.Channel.Write(normalized)
31+
32+	// Return the count based on original data length, not normalized
33+	if written > len(p) {
34+		written = len(p)
35+	}
36+	return written, err
37+}
38+
39 var _ context.Context = &SSHServerConnSession{}
40 
41 func (sc *SSHServerConn) Handle(chans <-chan ssh.NewChannel, reqs <-chan *ssh.Request) error {