repos / pico

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

commit
c79df32
parent
92b854a
author
Antonio Mika
date
2025-03-12 17:14:03 -0400 EDT
Updates
4 files changed,  +164, -44
M pssh/logger.go
+20, -6
 1@@ -45,24 +45,38 @@ func LogMiddleware(getLogger GetLoggerInterface, db FindUserInterface) SSHServer
 2 
 3 			pty, _, ok := s.Pty()
 4 
 5+			width, height := 0, 0
 6+			term := ""
 7+			if pty != nil {
 8+				term = pty.Term
 9+				width = pty.Window.Width
10+				height = pty.Window.Height
11+			}
12+
13 			logger.Info(
14 				"connect",
15 				"sshUser", s.User(),
16 				"pty", ok,
17-				"term", pty.Term,
18-				"windowWidth", pty.Window.Width,
19-				"windowHeight", pty.Window.Height,
20+				"term", term,
21+				"windowWidth", width,
22+				"windowHeight", height,
23 			)
24 
25 			sshHandler(s)
26 
27+			if pty != nil {
28+				term = pty.Term
29+				width = pty.Window.Width
30+				height = pty.Window.Height
31+			}
32+
33 			logger.Info(
34 				"disconnect",
35 				"sshUser", s.User(),
36 				"pty", ok,
37-				"term", pty.Term,
38-				"windowWidth", pty.Window.Width,
39-				"windowHeight", pty.Window.Height,
40+				"term", term,
41+				"windowWidth", width,
42+				"windowHeight", height,
43 				"duration", time.Since(ct),
44 			)
45 
M pssh/pty.go
+87, -0
 1@@ -1,6 +1,7 @@
 2 package pssh
 3 
 4 import (
 5+	"encoding/binary"
 6 	"fmt"
 7 )
 8 
 9@@ -33,3 +34,89 @@ func PtyMdw(mdw SSHServerMiddleware) SSHServerMiddleware {
10 		}
11 	}
12 }
13+
14+type Window struct {
15+	Width        int
16+	Height       int
17+	HeightPixels int
18+	WidthPixels  int
19+}
20+
21+type Pty struct {
22+	Term   string
23+	Window Window
24+}
25+
26+func (p *Pty) Resize(width, height int) error {
27+	return nil
28+}
29+
30+func (p *Pty) Name() string {
31+	return ""
32+}
33+
34+func parsePtyRequest(s []byte) (pty Pty, ok bool) {
35+	term, s, ok := parseString(s)
36+	if !ok {
37+		return
38+	}
39+	width32, s, ok := parseUint32(s)
40+	if !ok {
41+		return
42+	}
43+	height32, _, ok := parseUint32(s)
44+	if !ok {
45+		return
46+	}
47+	pty = Pty{
48+		Term: term,
49+		Window: Window{
50+			Width:  int(width32),
51+			Height: int(height32),
52+		},
53+	}
54+	return
55+}
56+
57+func parseWinchRequest(s []byte) (win Window, ok bool) {
58+	width32, s, ok := parseUint32(s)
59+	if width32 < 1 {
60+		ok = false
61+	}
62+	if !ok {
63+		return
64+	}
65+	height32, _, ok := parseUint32(s)
66+	if height32 < 1 {
67+		ok = false
68+	}
69+	if !ok {
70+		return
71+	}
72+	win = Window{
73+		Width:  int(width32),
74+		Height: int(height32),
75+	}
76+	return
77+}
78+
79+func parseString(in []byte) (out string, rest []byte, ok bool) {
80+	if len(in) < 4 {
81+		return
82+	}
83+	length := binary.BigEndian.Uint32(in)
84+	if uint32(len(in)) < 4+length {
85+		return
86+	}
87+	out = string(in[4 : 4+length])
88+	rest = in[4+length:]
89+	ok = true
90+	return
91+}
92+
93+func parseUint32(in []byte) (uint32, []byte, bool) {
94+	if len(in) < 4 {
95+		return 0, nil, false
96+	}
97+	return binary.BigEndian.Uint32(in), in[4:], true
98+}
M pssh/server.go
+55, -36
  1@@ -7,7 +7,6 @@ import (
  2 	"fmt"
  3 	"log/slog"
  4 	"net"
  5-	"os"
  6 	"strings"
  7 	"sync"
  8 	"time"
  9@@ -34,6 +33,11 @@ func (sc *SSHServerConn) Close() error {
 10 type SSHServerConnSession struct {
 11 	ssh.Channel
 12 	*SSHServerConn
 13+
 14+	pty   *Pty
 15+	winch chan Window
 16+
 17+	mu sync.Mutex
 18 }
 19 
 20 // Deadline implements context.Context.
 21@@ -130,29 +134,15 @@ func (s *SSHServerConnSession) Fatal(err error) {
 22 	_ = s.Close()
 23 }
 24 
 25-type Window struct {
 26-	Width        int
 27-	Height       int
 28-	HeightPixels int
 29-	WidthPixels  int
 30-}
 31-
 32-type Pty struct {
 33-	Term   string
 34-	Window Window
 35-	Slave  os.File
 36-}
 37-
 38-func (s *SSHServerConnSession) Pty() (Pty, <-chan Window, bool) {
 39-	return Pty{}, nil, false
 40-}
 41+func (s *SSHServerConnSession) Pty() (*Pty, <-chan Window, bool) {
 42+	s.mu.Lock()
 43+	defer s.mu.Unlock()
 44 
 45-func (p Pty) Resize(width, height int) error {
 46-	return nil
 47-}
 48+	if s.pty == nil {
 49+		return nil, nil, false
 50+	}
 51 
 52-func (p Pty) Name() string {
 53-	return ""
 54+	return s.pty, s.winch, true
 55 }
 56 
 57 var _ context.Context = &SSHServerConnSession{}
 58@@ -335,6 +325,11 @@ func NewSSHServer(ctx context.Context, logger *slog.Logger, config *SSHServerCon
 59 				return err
 60 			}
 61 
 62+			sesh := &SSHServerConnSession{
 63+				Channel:       channel,
 64+				SSHServerConn: sc,
 65+			}
 66+
 67 			for {
 68 				select {
 69 				case <-sc.Done():
 70@@ -346,7 +341,8 @@ func NewSSHServer(ctx context.Context, logger *slog.Logger, config *SSHServerCon
 71 
 72 					go func() {
 73 						sc.Logger.Info("new session request", "type", req.Type, "wantReply", req.WantReply, "payload", req.Payload)
 74-						if req.Type == "subsystem" {
 75+						switch req.Type {
 76+						case "subsystem":
 77 							if len(sc.SSHServer.Config.SubsystemMiddleware) == 0 {
 78 								req.Reply(false, nil)
 79 								sc.Close()
 80@@ -358,11 +354,6 @@ func NewSSHServer(ctx context.Context, logger *slog.Logger, config *SSHServerCon
 81 								h = m(h)
 82 							}
 83 
 84-							sesh := &SSHServerConnSession{
 85-								Channel:       channel,
 86-								SSHServerConn: sc,
 87-							}
 88-
 89 							req.Reply(true, nil)
 90 
 91 							if err := h(sesh); err != nil {
 92@@ -373,18 +364,15 @@ func NewSSHServer(ctx context.Context, logger *slog.Logger, config *SSHServerCon
 93 
 94 							sesh.Exit(0)
 95 							sesh.Close()
 96-							return
 97-						} else if req.Type == "exec" {
 98+						case "shell", "exec":
 99 							if len(sc.SSHServer.Config.Middleware) == 0 {
100 								req.Reply(false, nil)
101 							}
102 
103-							sesh := &SSHServerConnSession{
104-								Channel:       channel,
105-								SSHServerConn: sc,
106-							}
107+							var payload = struct{ Value string }{}
108+							ssh.Unmarshal(req.Payload, &payload)
109 
110-							sesh.SetValue("command", strings.Fields(string(req.Payload[4:])))
111+							sesh.SetValue("command", strings.Fields(payload.Value))
112 
113 							h := func(*SSHServerConnSession) error { return nil }
114 							for _, m := range sc.SSHServer.Config.Middleware {
115@@ -401,7 +389,38 @@ func NewSSHServer(ctx context.Context, logger *slog.Logger, config *SSHServerCon
116 
117 							sesh.Exit(0)
118 							sesh.Close()
119-							return
120+						case "pty-req":
121+							if sesh.pty != nil {
122+								req.Reply(false, nil)
123+								return
124+							}
125+
126+							ptyReq, ok := parsePtyRequest(req.Payload)
127+							if !ok {
128+								req.Reply(false, nil)
129+								return
130+							}
131+
132+							sesh.mu.Lock()
133+							sesh.pty = &ptyReq
134+							sesh.winch = make(chan Window, 1)
135+							sesh.mu.Unlock()
136+
137+							sesh.winch <- ptyReq.Window
138+							req.Reply(ok, nil)
139+						case "window-change":
140+							if sesh.pty == nil {
141+								req.Reply(false, nil)
142+								return
143+							}
144+							win, ok := parseWinchRequest(req.Payload)
145+							if ok {
146+								sesh.mu.Lock()
147+								sesh.pty.Window = win
148+								sesh.winch <- win
149+								sesh.mu.Unlock()
150+							}
151+							req.Reply(ok, nil)
152 						}
153 					}()
154 				}
M shared/senpai.go
+2, -2
 1@@ -17,7 +17,7 @@ type consoleData struct {
 2 
 3 type VConsole struct {
 4 	Session *pssh.SSHServerConnSession
 5-	pty     pssh.Pty
 6+	pty     *pssh.Pty
 7 
 8 	sizeEnableOnce sync.Once
 9 
10@@ -108,7 +108,7 @@ func (v *VConsole) Size() (console.WinSize, error) {
11 }
12 
13 func (v *VConsole) Fd() uintptr {
14-	return v.pty.Slave.Fd()
15+	return 0
16 }
17 
18 func (v *VConsole) Name() string {