Source file
src/os/pidfd_linux.go
Documentation: os
1
2
3
4
5
6
7
8
9
10
11
12 package os
13
14 import (
15 "errors"
16 "internal/syscall/unix"
17 "sync"
18 "syscall"
19 "unsafe"
20 )
21
22
23
24
25 func ensurePidfd(sysAttr *syscall.SysProcAttr) (*syscall.SysProcAttr, bool) {
26 if !pidfdWorks() {
27 return sysAttr, false
28 }
29
30 var pidfd int
31
32 if sysAttr == nil {
33 return &syscall.SysProcAttr{
34 PidFD: &pidfd,
35 }, false
36 }
37 if sysAttr.PidFD == nil {
38 newSys := *sysAttr
39 newSys.PidFD = &pidfd
40 return &newSys, false
41 }
42
43 return sysAttr, true
44 }
45
46
47
48 func getPidfd(sysAttr *syscall.SysProcAttr, needDup bool) (uintptr, bool) {
49 if !pidfdWorks() {
50 return 0, false
51 }
52
53 h := *sysAttr.PidFD
54 if needDup {
55 dupH, e := unix.Fcntl(h, syscall.F_DUPFD_CLOEXEC, 0)
56 if e != nil {
57 return 0, false
58 }
59 h = dupH
60 }
61 return uintptr(h), true
62 }
63
64 func pidfdFind(pid int) (uintptr, error) {
65 if !pidfdWorks() {
66 return 0, syscall.ENOSYS
67 }
68
69 h, err := unix.PidFDOpen(pid, 0)
70 if err != nil {
71 return 0, convertESRCH(err)
72 }
73 return h, nil
74 }
75
76
77 const _P_PIDFD = 3
78
79 func (p *Process) pidfdWait() (*ProcessState, error) {
80
81
82
83
84
85
86
87 handle, status := p.handleTransientAcquire()
88 switch status {
89 case statusDone:
90
91
92
93 return nil, NewSyscallError("wait", syscall.ECHILD)
94 case statusReleased:
95 return nil, syscall.EINVAL
96 }
97 defer p.handleTransientRelease()
98
99 var (
100 info unix.SiginfoChild
101 rusage syscall.Rusage
102 e syscall.Errno
103 )
104 for {
105 _, _, e = syscall.Syscall6(syscall.SYS_WAITID, _P_PIDFD, handle, uintptr(unsafe.Pointer(&info)), syscall.WEXITED, uintptr(unsafe.Pointer(&rusage)), 0)
106 if e != syscall.EINTR {
107 break
108 }
109 }
110 if e != 0 {
111 return nil, NewSyscallError("waitid", e)
112 }
113
114
115 p.handlePersistentRelease(statusDone)
116 return &ProcessState{
117 pid: int(info.Pid),
118 status: info.WaitStatus(),
119 rusage: &rusage,
120 }, nil
121 }
122
123 func (p *Process) pidfdSendSignal(s syscall.Signal) error {
124 handle, status := p.handleTransientAcquire()
125 switch status {
126 case statusDone:
127 return ErrProcessDone
128 case statusReleased:
129 return errors.New("os: process already released")
130 }
131 defer p.handleTransientRelease()
132
133 return convertESRCH(unix.PidFDSendSignal(handle, s))
134 }
135
136 func pidfdWorks() bool {
137 return checkPidfdOnce() == nil
138 }
139
140 var checkPidfdOnce = sync.OnceValue(checkPidfd)
141
142
143
144
145
146
147
148
149 func checkPidfd() error {
150
151
152 fd, err := unix.PidFDOpen(syscall.Getpid(), 0)
153 if err != nil {
154 return NewSyscallError("pidfd_open", err)
155 }
156 defer syscall.Close(int(fd))
157
158
159 for {
160 _, _, err = syscall.Syscall6(syscall.SYS_WAITID, _P_PIDFD, fd, 0, syscall.WEXITED, 0, 0)
161 if err != syscall.EINTR {
162 break
163 }
164 }
165
166 if err != syscall.ECHILD {
167 return NewSyscallError("pidfd_wait", err)
168 }
169
170
171 if err := unix.PidFDSendSignal(fd, 0); err != nil {
172 return NewSyscallError("pidfd_send_signal", err)
173 }
174
175 return nil
176 }
177
View as plain text