mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
146 lines
3.5 KiB
Go
146 lines
3.5 KiB
Go
|
|
// Copyright 2023 The Go Authors. All rights reserved.
|
||
|
|
// Use of this source code is governed by a BSD-style
|
||
|
|
// license that can be found in the LICENSE file.
|
||
|
|
|
||
|
|
//go:build linux
|
||
|
|
|
||
|
|
// Support for pidfd was added during the course of a few Linux releases:
|
||
|
|
// v5.1: pidfd_send_signal syscall;
|
||
|
|
// v5.2: CLONE_PIDFD flag for clone syscall;
|
||
|
|
// v5.3: pidfd_open syscall, clone3 syscall;
|
||
|
|
// v5.4: P_PIDFD idtype support for waitid syscall;
|
||
|
|
// v5.6: pidfd_getfd syscall.
|
||
|
|
|
||
|
|
package os
|
||
|
|
|
||
|
|
import (
|
||
|
|
"internal/syscall/unix"
|
||
|
|
"sync"
|
||
|
|
"syscall"
|
||
|
|
"unsafe"
|
||
|
|
)
|
||
|
|
|
||
|
|
func ensurePidfd(sysAttr *syscall.SysProcAttr) *syscall.SysProcAttr {
|
||
|
|
if !pidfdWorks() {
|
||
|
|
return sysAttr
|
||
|
|
}
|
||
|
|
|
||
|
|
var pidfd int
|
||
|
|
|
||
|
|
if sysAttr == nil {
|
||
|
|
return &syscall.SysProcAttr{
|
||
|
|
PidFD: &pidfd,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if sysAttr.PidFD == nil {
|
||
|
|
newSys := *sysAttr // copy
|
||
|
|
newSys.PidFD = &pidfd
|
||
|
|
return &newSys
|
||
|
|
}
|
||
|
|
|
||
|
|
return sysAttr
|
||
|
|
}
|
||
|
|
|
||
|
|
func getPidfd(sysAttr *syscall.SysProcAttr) uintptr {
|
||
|
|
if !pidfdWorks() {
|
||
|
|
return unsetHandle
|
||
|
|
}
|
||
|
|
|
||
|
|
return uintptr(*sysAttr.PidFD)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (p *Process) pidfdRelease() {
|
||
|
|
// Release pidfd unconditionally.
|
||
|
|
handle := p.handle.Swap(unsetHandle)
|
||
|
|
if handle != unsetHandle {
|
||
|
|
syscall.Close(int(handle))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// _P_PIDFD is used as idtype argument to waitid syscall.
|
||
|
|
const _P_PIDFD = 3
|
||
|
|
|
||
|
|
func (p *Process) pidfdWait() (*ProcessState, error) {
|
||
|
|
handle := p.handle.Load()
|
||
|
|
if handle == unsetHandle || !pidfdWorks() {
|
||
|
|
return nil, syscall.ENOSYS
|
||
|
|
}
|
||
|
|
var (
|
||
|
|
info unix.SiginfoChild
|
||
|
|
rusage syscall.Rusage
|
||
|
|
e syscall.Errno
|
||
|
|
)
|
||
|
|
for {
|
||
|
|
_, _, e = syscall.Syscall6(syscall.SYS_WAITID, _P_PIDFD, handle, uintptr(unsafe.Pointer(&info)), syscall.WEXITED, uintptr(unsafe.Pointer(&rusage)), 0)
|
||
|
|
if e != syscall.EINTR {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if e != 0 {
|
||
|
|
if e == syscall.EINVAL {
|
||
|
|
// This is either invalid option value (which should not happen
|
||
|
|
// as we only use WEXITED), or missing P_PIDFD support (Linux
|
||
|
|
// kernel < 5.4), meaning pidfd support is not implemented.
|
||
|
|
e = syscall.ENOSYS
|
||
|
|
}
|
||
|
|
return nil, e
|
||
|
|
}
|
||
|
|
p.setDone()
|
||
|
|
defer p.pidfdRelease()
|
||
|
|
return &ProcessState{
|
||
|
|
pid: int(info.Pid),
|
||
|
|
status: info.WaitStatus(),
|
||
|
|
rusage: &rusage,
|
||
|
|
}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (p *Process) pidfdSendSignal(s syscall.Signal) error {
|
||
|
|
handle := p.handle.Load()
|
||
|
|
if handle == unsetHandle || !pidfdWorks() {
|
||
|
|
return syscall.ENOSYS
|
||
|
|
}
|
||
|
|
return convertESRCH(unix.PidFDSendSignal(handle, s))
|
||
|
|
}
|
||
|
|
|
||
|
|
func pidfdWorks() bool {
|
||
|
|
return checkPidfdOnce() == nil
|
||
|
|
}
|
||
|
|
|
||
|
|
var checkPidfdOnce = sync.OnceValue(checkPidfd)
|
||
|
|
|
||
|
|
// checkPidfd checks whether all required pidfd-related syscalls work.
|
||
|
|
// This consists of pidfd_open and pidfd_send_signal syscalls, and waitid
|
||
|
|
// syscall with idtype of P_PIDFD.
|
||
|
|
//
|
||
|
|
// Reasons for non-working pidfd syscalls include an older kernel and an
|
||
|
|
// execution environment in which the above system calls are restricted by
|
||
|
|
// seccomp or a similar technology.
|
||
|
|
func checkPidfd() error {
|
||
|
|
// Get a pidfd of the current process (opening of "/proc/self" won't
|
||
|
|
// work for waitid).
|
||
|
|
fd, err := unix.PidFDOpen(syscall.Getpid(), 0)
|
||
|
|
if err != nil {
|
||
|
|
return NewSyscallError("pidfd_open", err)
|
||
|
|
}
|
||
|
|
defer syscall.Close(int(fd))
|
||
|
|
|
||
|
|
// Check waitid(P_PIDFD) works.
|
||
|
|
for {
|
||
|
|
_, _, err = syscall.Syscall6(syscall.SYS_WAITID, _P_PIDFD, fd, 0, syscall.WEXITED, 0, 0)
|
||
|
|
if err != syscall.EINTR {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// Expect ECHILD from waitid since we're not our own parent.
|
||
|
|
if err != syscall.ECHILD {
|
||
|
|
return NewSyscallError("pidfd_wait", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check pidfd_send_signal works (should be able to send 0 to itself).
|
||
|
|
if err := unix.PidFDSendSignal(fd, 0); err != nil {
|
||
|
|
return NewSyscallError("pidfd_send_signal", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|