Isolate routing fixtures from host Screen sessions
This commit is contained in:
1 parent
0b3ba7fbe7
commit
e6da76e14a
3 files changed
+60
-3
No files matched your search
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in new issue
Block a user