backend,termstatus: Unify foreground/background detection

PR #5358 reintroduced a version of the TIOCGPGRP ioctl call that works
on all Unix platforms except Linux, due to a bug/inconsistency in
x/sys/unix. This commit fixes that by introducing termstatus.Tcgetpgrp.

It also introduces termstatus.Getpgrp and termstatus.Tcsetpgrp to deal
with the different signature of unix.Getpgrp in Solaris vs. all other
Unix platforms and an int-overflowing constant on AIX, so that some
AIX/Solaris-specific code can be removed elsewhere and
foreground/background detection is done the same everywhere except on
Windows.
This commit is contained in:
greatroar
2025-09-07 12:57:01 +02:00
parent 42f690dbab
commit 1ed93bd54d
12 changed files with 89 additions and 76 deletions

View File

@@ -1,27 +0,0 @@
package termstatus
import (
"github.com/restic/restic/internal/debug"
"golang.org/x/sys/unix"
)
// IsProcessBackground reports whether the current process is running in the
// background. fd must be a file descriptor for the terminal.
func IsProcessBackground(fd uintptr) bool {
bg, err := isProcessBackground(fd)
if err != nil {
debug.Log("Can't check if we are in the background. Using default behaviour. Error: %s\n", err.Error())
return false
}
return bg
}
func isProcessBackground(fd uintptr) (bool, error) {
// We need to use IoctlGetUint32 here, because pid_t is 32-bit even on
// 64-bit Linux. IoctlGetInt doesn't work on big-endian platforms:
// https://github.com/golang/go/issues/45585
// https://github.com/golang/go/issues/60429
pid, err := unix.IoctlGetUint32(int(fd), unix.TIOCGPGRP)
return int(pid) != unix.Getpgrp(), err
}

View File

@@ -0,0 +1,24 @@
//go:build unix
package termstatus
import "github.com/restic/restic/internal/debug"
// IsProcessBackground reports whether the current process is running in the
// background. fd must be a file descriptor for the terminal.
func IsProcessBackground(fd uintptr) bool {
bg, err := isProcessBackground(int(fd))
if err != nil {
debug.Log("Can't check if we are in the background. Using default behaviour. Error: %s\n", err.Error())
return false
}
return bg
}
func isProcessBackground(fd int) (bg bool, err error) {
pgid, err := Tcgetpgrp(fd)
if err != nil {
return false, err
}
return pgid != Getpgrp(), nil
}

View File

@@ -1,3 +1,5 @@
//go:build unix
package termstatus
import (
@@ -13,7 +15,7 @@ func TestIsProcessBackground(t *testing.T) {
t.Skipf("can't open terminal: %v", err)
}
_, err = isProcessBackground(tty.Fd())
_, err = isProcessBackground(int(tty.Fd()))
rtest.OK(t, err)
_ = tty.Close()

View File

@@ -1,6 +1,3 @@
//go:build !linux
// +build !linux
package termstatus
// IsProcessBackground reports whether the current process is running in the

View File

@@ -0,0 +1,8 @@
package termstatus
import "golang.org/x/sys/unix"
func Getpgrp() int {
pid, _ := unix.Getpgrp()
return pid
}

View File

@@ -0,0 +1,7 @@
//go:build unix && !solaris
package termstatus
import "golang.org/x/sys/unix"
func Getpgrp() int { return unix.Getpgrp() }

View File

@@ -0,0 +1,12 @@
package termstatus
import "golang.org/x/sys/unix"
func Tcgetpgrp(ttyfd int) (int, error) {
// We need to use IoctlGetUint32 here, because pid_t is 32-bit even on
// 64-bit Linux. IoctlGetInt doesn't work on big-endian platforms:
// https://github.com/golang/go/issues/45585
// https://github.com/golang/go/issues/60429
pid, err := unix.IoctlGetUint32(ttyfd, unix.TIOCGPGRP)
return int(pid), err
}

View File

@@ -0,0 +1,9 @@
//go:build unix && !linux
package termstatus
import "golang.org/x/sys/unix"
func Tcgetpgrp(ttyfd int) (int, error) {
return unix.IoctlGetInt(ttyfd, unix.TIOCGPGRP)
}

View File

@@ -0,0 +1,10 @@
package termstatus
import "golang.org/x/sys/unix"
func Tcsetpgrp(fd int, pid int) error {
// The second argument to IoctlSetPointerInt has type int on AIX,
// but the constant overflows 64-bit int, hence the two-step cast.
req := uint(unix.TIOCSPGRP)
return unix.IoctlSetPointerInt(fd, int(req), pid)
}

View File

@@ -0,0 +1,9 @@
//go:build unix && !aix
package termstatus
import "golang.org/x/sys/unix"
func Tcsetpgrp(fd int, pid int) error {
return unix.IoctlSetPointerInt(fd, unix.TIOCSPGRP, pid)
}