-
Notifications
You must be signed in to change notification settings - Fork 2
Interactive serial module #282
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
87a4da9
0c18ab6
20296f2
a68f4a1
028000f
fc20c17
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,7 +5,6 @@ | |||||||||
| package main | ||||||||||
|
|
||||||||||
| import ( | ||||||||||
| "bufio" | ||||||||||
| "context" | ||||||||||
| "errors" | ||||||||||
| "fmt" | ||||||||||
|
|
@@ -17,6 +16,7 @@ import ( | |||||||||
|
|
||||||||||
| "connectrpc.com/connect" | ||||||||||
| "github.com/BlindspotSoftware/dutctl/internal/output" | ||||||||||
| "golang.org/x/sys/unix" | ||||||||||
|
|
||||||||||
| pb "github.com/BlindspotSoftware/dutctl/protobuf/gen/dutctl/v1" | ||||||||||
| ) | ||||||||||
|
|
@@ -92,10 +92,45 @@ func (app *application) detailsRPC(device, command, keyword string) error { | |||||||||
| return nil | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // setRawInput puts the terminal into raw input mode: disables local echo and | ||||||||||
| // canonical (line-buffered) mode so each keystroke is available immediately. | ||||||||||
| // It returns a restore function, or nil if the fd is not a terminal. | ||||||||||
| func setRawInput(fileDescriptor int) func() { | ||||||||||
| termios, err := unix.IoctlGetTermios(fileDescriptor, unix.TCGETS) | ||||||||||
| if err != nil { | ||||||||||
| return nil | ||||||||||
| } | ||||||||||
|
llogen marked this conversation as resolved.
|
||||||||||
|
|
||||||||||
| old := *termios | ||||||||||
|
|
||||||||||
| termios.Iflag &^= unix.ICRNL | ||||||||||
| termios.Lflag &^= unix.ECHO | unix.ICANON | ||||||||||
| termios.Cc[unix.VMIN] = 1 | ||||||||||
| termios.Cc[unix.VTIME] = 0 | ||||||||||
|
|
||||||||||
| err = unix.IoctlSetTermios(fileDescriptor, unix.TCSETS, termios) | ||||||||||
| if err != nil { | ||||||||||
| return nil | ||||||||||
| } | ||||||||||
|
|
||||||||||
| return func() { | ||||||||||
| _ = unix.IoctlSetTermios(fileDescriptor, unix.TCSETS, &old) | ||||||||||
|
||||||||||
| _ = unix.IoctlSetTermios(fileDescriptor, unix.TCSETS, &old) | |
| if err := unix.IoctlSetTermios(fileDescriptor, unix.TCSETS, &old); err != nil { | |
| log.Printf("warning: failed to restore terminal settings; you may need to run 'reset' or 'stty sane': %v", err) | |
| } |
Copilot
AI
Feb 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential bug: The buffer is reused across iterations, but buf[:nRead] is sent directly in the protobuf message. If the protobuf library doesn't copy the data immediately, this could cause data corruption when the buffer is overwritten by the next Read call. The serial module correctly copies the data (lines 160-161 in serial.go), but this code directly slices the buffer. Verify that the protobuf Send operation copies the data, or allocate a new slice for each send.
Copilot
AI
Feb 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The protobuf message stores buf[:nRead], but buf is reused on the next loop iteration. If the RPC implementation marshals/sends asynchronously, later reads could mutate the slice before it’s fully sent, corrupting stdin data. Safer is to copy the read bytes into a new slice per message before calling stream.Send.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -45,8 +45,8 @@ | |||||
| // for module execution and a channel signaling worker termination or errors. | ||||||
| // Multiple calls are idempotent; subsequent calls return the already initialized session and channel. | ||||||
| // | ||||||
| //nolint:ireturn // returning interface module.Session is intentional for abstraction boundary | ||||||
|
|
||||||
|
||||||
| //nolint:ireturn // Start intentionally returns module.Session (an interface) as part of the public API. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -100,6 +100,10 @@ func toClientWorker(ctx context.Context, stream Stream, s *session) error { | |
| // | ||
| //nolint:cyclop,funlen,gocognit | ||
| func fromClientWorker(ctx context.Context, stream Stream, s *session) error { | ||
| // Close stdinCh on exit so ChanReader.Read returns io.EOF and any module | ||
| // goroutine blocked on stdin (e.g. the serial inner goroutine) unblocks cleanly. | ||
| defer close(s.stdinCh) | ||
|
Comment on lines
+103
to
+105
|
||
|
|
||
| type recvResult struct { | ||
| req *pb.RunRequest | ||
| err error | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use of golang.org/x/sys/unix package makes this code Unix-specific (Linux, macOS, BSD, etc.) and will fail to compile on Windows. The setRawInput function uses Unix-specific terminal ioctl calls (TCGETS, TCSETS) that are not available on Windows. If cross-platform support is a goal, consider adding build tags (e.g., //go:build unix) to restrict this file to Unix systems, and provide a Windows-specific implementation or gracefully handle the lack of raw mode on Windows. If Windows support is not needed, this is acceptable but should be documented.