Isolate routing fixtures from host Screen sessions

This commit is contained in:
ak47 committed 2026-11-07 16:56:28 -05:00
1 parent 0b3ba7fbe7
commit e6da76e14a
3 files changed
+60 -3

No files matched your search

+7 -1
View File
@@ -94,7 +94,13 @@ ancestry:
}
}
readers := append(slices.Clone(terminals), consoles...)
if reader, handled, err := attached_screen_reader("/proc", ancestors, uid, readers); handled {
lookup := s.screenReader
if lookup == nil {
lookup = func(ancestors []int, uid int, readers []instance) (instance, bool, error) {
return attached_screen_reader("/proc", ancestors, uid, readers)
}
}
if reader, handled, err := lookup(ancestors, uid, readers); handled {
if err != nil {
return "", err
}
+43 -1
View File
@@ -14,6 +14,7 @@ import (
"os/exec"
"path/filepath"
"strings"
"syscall"
"testing"
"time"
)
@@ -66,13 +67,54 @@ func TestControlProcess(t *testing.T) {
func child_command(mode string) *exec.Cmd {
command := exec.Command(os.Args[0], "-test.run=^TestControlProcess$")
command.Env = append(os.Environ(), "FENRIR_CONTROL_PROCESS="+mode)
// A fixture must not inherit the developer's real VT as its controlling TTY.
command.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
return command
}
// Screen daemons launched by a fixture may be reparented to PID 1 and never
// reach the coordinator; their full ancestry still belongs to that fixture.
func fixture_ancestors(ancestors []int, coordinator int) []int {
for i, pid := range ancestors {
if pid == coordinator {
return ancestors[:i]
}
}
return ancestors
}
func TestFixtureScreenInspectionExcludesHostAndKeepsPrivateSessions(t *testing.T) {
for _, trial := range []struct {
name string
ancestors []int
handled bool
}{
{"host Screen above coordinator", []int{120, 300, 100}, false},
{"private Screen below coordinator", []int{120, 100, 300}, true},
{"private Screen reparented to init", []int{120, 100}, true},
{"coordinator caller", []int{300, 100}, false},
} {
t.Run(trial.name, func(t *testing.T) {
f := new_screen_fixture(t)
f.attach(200, 300, "/dev/null", true)
reader, handled, err := attached_screen_reader(f.root, fixture_ancestors(trial.ancestors, 300), os.Geteuid(), []instance{{PID: 300}})
if err != nil || handled != trial.handled || handled && reader.PID != 300 {
t.Fatalf("reader=%+v handled=%v err=%v; want handled=%v", reader, handled, err, trial.handled)
}
})
}
}
func run_reader_process(mode string) {
encoder := json.NewEncoder(os.Stdout)
directory := os.Getenv("FENRIR_CONTROL_DIRECTORY")
server, err := Open(context.Background(), Options{Directory: directory, RegistryDirectory: filepath.Join(directory, "registry"), TerminalMode: mode == "terminal", WindowID: os.Getenv("WINDOWID")})
// Processes above the test coordinator belong to the host, not this fixture.
// Screen sessions created by the native test remain inside the inspected tree.
coordinator := os.Getppid()
lookup := func(ancestors []int, uid int, readers []instance) (instance, bool, error) {
return attached_screen_reader("/proc", fixture_ancestors(ancestors, coordinator), uid, readers)
}
server, err := open_server(context.Background(), Options{Directory: directory, RegistryDirectory: filepath.Join(directory, "registry"), TerminalMode: mode == "terminal", WindowID: os.Getenv("WINDOWID")}, lookup)
if err != nil {
_ = encoder.Encode(processReply{Error: err.Error()})
return
+10 -1
View File
@@ -59,6 +59,8 @@ type endpoint struct {
info os.FileInfo
}
type screenLookup func(ancestors []int, uid int, readers []instance) (instance, bool, error)
type Server struct {
ctx context.Context
cancel context.CancelFunc
@@ -69,6 +71,7 @@ type Server struct {
workers sync.WaitGroup
clients chan struct{}
registryPath string
screenReader screenLookup
stopOnce sync.Once
done chan struct{}
}
@@ -81,8 +84,14 @@ func (s *Server) main_path() string {
}
func Open(ctx context.Context, options Options) (*Server, error) {
return open_server(ctx, options, nil)
}
// Keep process inspection injectable so fixture readers can exclude the host's
// terminal ancestry while retaining real sockets, credentials and handoffs.
func open_server(ctx context.Context, options Options, lookup screenLookup) (*Server, error) {
ctx, cancel := context.WithCancel(ctx)
s := &Server{ctx: ctx, cancel: cancel, options: options, requests: make(chan Request, 32), clients: make(chan struct{}, 8), done: make(chan struct{})}
s := &Server{ctx: ctx, cancel: cancel, options: options, screenReader: lookup, requests: make(chan Request, 32), clients: make(chan struct{}, 8), done: make(chan struct{})}
paths := []string{filepath.Join(filepath.Dir(s.main_path()), fmt.Sprintf("fenrirscreenreader-%d.sock", os.Getpid())), s.main_path()}
if options.SocketFile != "" {
paths = []string{options.SocketFile}