]> Cypherpunks repositories - gostls13.git/commitdiff
os: make FindProcess use pidfd on Linux
authorKir Kolyshkin <kolyshkin@gmail.com>
Thu, 16 Nov 2023 09:42:39 +0000 (01:42 -0800)
committerMichael Pratt <mpratt@google.com>
Wed, 21 Feb 2024 21:27:03 +0000 (21:27 +0000)
Amend FindProcess to use pidfdFind, and make it return ErrProcessDone
if pidfdFind is used and the process is not found.

Since this is a change in API, introduce GODEBUG osfinderr=0 setting
to disable the feature.

Change-Id: I724c6f622f0c99f21a70b864cf7cf2b8836869ee
Reviewed-on: https://go-review.googlesource.com/c/go/+/542699
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
doc/godebug.md
src/internal/godebugs/table.go
src/os/exec.go
src/os/exec_unix.go
src/os/pidfd_linux.go
src/os/pidfd_other.go
src/runtime/metrics/doc.go

index a7619c9a3da48a7fa289bf5a9508ad6e2987eafe..9dbbab284dbef0235d15611c5d1ffc77fd24c338 100644 (file)
@@ -126,6 +126,11 @@ for example,
 see the [runtime documentation](/pkg/runtime#hdr-Environment_Variables)
 and the [go command documentation](/cmd/go#hdr-Build_and_test_caching).
 
+### Go 1.23
+
+Go 1.23 enabled Linux pidfd support for process lookup. This feature can be
+disabled by using the [`osfinderr` setting](/pkg/os#FindProcess).
+
 ### Go 1.22
 
 Go 1.22 adds a configurable limit to control the maximum acceptable RSA key size
index a0a067296686419e57545cf41d3581b6163c640d..4ac306b323dfeb005e818bd7e2a462a72f6dd873 100644 (file)
@@ -42,6 +42,7 @@ var All = []Info{
        {Name: "multipartmaxparts", Package: "mime/multipart"},
        {Name: "multipathtcp", Package: "net"},
        {Name: "netdns", Package: "net", Opaque: true},
+       {Name: "osfinderr", Package: "os"},
        {Name: "panicnil", Package: "runtime", Changed: 21, Old: "1"},
        {Name: "randautoseed", Package: "math/rand"},
        {Name: "tarinsecurepath", Package: "archive/tar"},
index 42e8a399a9878c15cfa6e5f38bf21ede6a7d7c1f..7ef1fee595b379083390df4a95811d912b6a25f6 100644 (file)
@@ -86,10 +86,17 @@ func Getppid() int { return syscall.Getppid() }
 // The Process it returns can be used to obtain information
 // about the underlying operating system process.
 //
-// On Unix systems, FindProcess always succeeds and returns a Process
+// On Unix systems other than Linux, FindProcess always succeeds and returns a Process
 // for the given pid, regardless of whether the process exists. To test whether
 // the process actually exists, see whether p.Signal(syscall.Signal(0)) reports
 // an error.
+//
+// On Linux, FindProcess may either return ErrProcessGone for a non-existing
+// process (thus eliminating the need to use a signal to check if the process
+// exists), or work the same way as for other Unix systems, described above,
+// depending on the kernel version used and the system configuration. The old
+// behavior (of always succeeding) can be enforced by using GODEBUG setting
+// osfinderr=0.
 func FindProcess(pid int) (*Process, error) {
        return findProcess(pid)
 }
index 2c66a8be20325022a4b529445e24be0c49dfd240..21d03da48ecf397e9e0939aa45e24aa6e8df0e3b 100644 (file)
@@ -106,8 +106,14 @@ func (p *Process) release() error {
 }
 
 func findProcess(pid int) (p *Process, err error) {
-       // NOOP for unix.
-       return newProcess(pid, unsetHandle), nil
+       h, err := pidfdFind(pid)
+       if err == ErrProcessDone {
+               return nil, err
+       }
+       // Ignore all other errors from pidfdFind,
+       // as the callers do not expect them, and
+       // we can use pid anyway.
+       return newProcess(pid, h), nil
 }
 
 func (p *ProcessState) userTime() time.Duration {
index d6e1d53eee8e26e0624ad0f3e868af9d38ee37bb..cc67dfa05f662cfcb1b541277600f243c5f1fae0 100644 (file)
@@ -14,6 +14,7 @@
 package os
 
 import (
+       "internal/godebug"
        "internal/syscall/unix"
        "sync"
        "syscall"
@@ -49,6 +50,25 @@ func getPidfd(sysAttr *syscall.SysProcAttr) uintptr {
        return uintptr(*sysAttr.PidFD)
 }
 
+var osfinderr = godebug.New("osfinderr")
+
+func pidfdFind(pid int) (uintptr, error) {
+       if !pidfdWorks() {
+               return unsetHandle, syscall.ENOSYS
+       }
+       if osfinderr.Value() == "0" {
+               osfinderr.IncNonDefault()
+               return unsetHandle, syscall.ENOSYS
+
+       }
+
+       h, err := unix.PidFDOpen(pid, 0)
+       if err == nil {
+               return h, nil
+       }
+       return unsetHandle, convertESRCH(err)
+}
+
 func (p *Process) pidfdRelease() {
        // Release pidfd unconditionally.
        handle := p.handle.Swap(unsetHandle)
index 1918acbec5b7658e55224cdeebb2dfb01cbb2e44..bb38c724042bc27c545dce71b241a8ec5e6e87db 100644 (file)
@@ -16,6 +16,10 @@ func getPidfd(_ *syscall.SysProcAttr) uintptr {
        return unsetHandle
 }
 
+func pidfdFind(_ int) (uintptr, error) {
+       return unsetHandle, syscall.ENOSYS
+}
+
 func (p *Process) pidfdRelease() {}
 
 func (_ *Process) pidfdWait() (*ProcessState, error) {
index fb2f44da29c55df23e182a0e397be336594a4fb0..d1c2f8aa25c544b609a4a8673b41a521de7a4087 100644 (file)
@@ -290,6 +290,10 @@ Below is the full list of supported metrics, ordered lexicographically.
                The number of non-default behaviors executed by the net package
                due to a non-default GODEBUG=multipathtcp=... setting.
 
+       /godebug/non-default-behavior/osfinderr:events
+               The number of non-default behaviors executed by the os package
+               due to a non-default GODEBUG=osfinderr=... setting.
+
        /godebug/non-default-behavior/panicnil:events
                The number of non-default behaviors executed by the runtime
                package due to a non-default GODEBUG=panicnil=... setting.