carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1//go:build linux
2
3package activator
4
5import (
6 "errors"
7 "os"
8 "syscall"
9
10 "golang.org/x/sys/unix"
11
12 "github.com/carlosframework/platform/internal/hostbox"
13)
14
15// pinRootProcess returns the process root granted at pid, pinned by a pidfd
16// that was opened before its start was compared with start. The pin, not the
17// pid, is what the watcher follows, so a reused pid can never stand in for
18// the grant's process. A pid that is already gone (ESRCH) is the grant's
19// process having exited and returns a process that reads as exited. A pidfd
20// that cannot be opened for any other reason is an error, never an exit.
21func pinRootProcess(pid int, start string) (*os.Process, error) {
22 process, err := os.FindProcess(pid)
23 if err != nil {
24 return nil, err
25 }
26 pinned := false
27 err = process.WithHandle(func(fd uintptr) {
28 got, e := roleProcessStart(pid)
29 // The start was read through the pid. It is the pinned process's
30 // only if that process had not exited by the time it was read.
31 pinned = e == nil && got == start && !pidfdExited(int(fd))
32 })
33 switch {
34 case processGone(process, err):
35 return process, nil
36 case err != nil:
37 _ = process.Release()
38 return nil, errors.Join(hostbox.ErrChanged, err)
39 case !pinned:
40 _ = process.Release()
41 return nil, hostbox.ErrChanged
42 }
43 return process, nil
44}
45
46// processGone reports whether err, from process's WithHandle, means the
47// process has exited. A process found after its pid was gone (ESRCH) has no
48// handle and answers Signal without a pidfd, so its pid is asked directly.
49func processGone(process *os.Process, err error) bool {
50 if errors.Is(err, os.ErrNoHandle) {
51 err = process.Signal(syscall.Signal(0))
52 }
53 return errors.Is(err, os.ErrProcessDone)
54}
55
56// pidfdExited reports whether the process behind fd has exited, without
57// waiting.
58func pidfdExited(fd int) bool {
59 fds := []unix.PollFd{{Fd: int32(fd), Events: unix.POLLIN}}
60 for {
61 n, err := unix.Poll(fds, 0)
62 if errors.Is(err, unix.EINTR) {
63 continue
64 }
65 return err != nil || n > 0
66 }
67}
68
69// watchRootProcess closes done when process exits. The agent is not the
70// process's parent, so it polls the pidfd process was pinned by rather than
71// waiting. A process with no pinned pidfd cannot be watched, and done then
72// stays open: an unwatchable process is never reported as exited.
73func watchRootProcess(process *os.Process, done chan struct{}) {
74 go func() {
75 exited := false
76 err := process.WithHandle(func(fd uintptr) {
77 fds := []unix.PollFd{{Fd: int32(fd), Events: unix.POLLIN}}
78 for {
79 n, err := unix.Poll(fds, -1)
80 if errors.Is(err, unix.EINTR) {
81 continue
82 }
83 exited = err == nil && n > 0
84 return
85 }
86 })
87 if exited || processGone(process, err) {
88 close(done)
89 }
90 }()
91}
92