mirror of
https://github.com/restic/restic.git
synced 2025-12-13 14:12:23 +00:00
Merge pull request #5493 from greatroar/ioctl
backend,termstatus: Unify foreground/background detection
This commit is contained in:
@@ -1,28 +0,0 @@
|
|||||||
//go:build aix || solaris
|
|
||||||
// +build aix solaris
|
|
||||||
|
|
||||||
package util
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os/exec"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/restic/restic/internal/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func startForeground(cmd *exec.Cmd) (bg func() error, err error) {
|
|
||||||
// run the command in its own process group so that SIGINT
|
|
||||||
// is not sent to it.
|
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
|
||||||
Setpgid: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// start the process
|
|
||||||
err = cmd.Start()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "cmd.Start")
|
|
||||||
}
|
|
||||||
|
|
||||||
bg = func() error { return nil }
|
|
||||||
return bg, nil
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
//go:build !aix && !solaris && !windows
|
//go:build unix
|
||||||
// +build !aix,!solaris,!windows
|
|
||||||
|
|
||||||
package util
|
package util
|
||||||
|
|
||||||
@@ -10,20 +9,11 @@ import (
|
|||||||
|
|
||||||
"github.com/restic/restic/internal/debug"
|
"github.com/restic/restic/internal/debug"
|
||||||
"github.com/restic/restic/internal/errors"
|
"github.com/restic/restic/internal/errors"
|
||||||
|
"github.com/restic/restic/internal/ui/termstatus"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
func tcgetpgrp(fd int) (int, error) {
|
|
||||||
return unix.IoctlGetInt(fd, unix.TIOCGPGRP)
|
|
||||||
}
|
|
||||||
|
|
||||||
func tcsetpgrp(fd int, pid int) error {
|
|
||||||
// IoctlSetPointerInt silently casts to int32 internally,
|
|
||||||
// so this assumes pid fits in 31 bits.
|
|
||||||
return unix.IoctlSetPointerInt(fd, unix.TIOCSPGRP, pid)
|
|
||||||
}
|
|
||||||
|
|
||||||
func startForeground(cmd *exec.Cmd) (bg func() error, err error) {
|
func startForeground(cmd *exec.Cmd) (bg func() error, err error) {
|
||||||
// run the command in its own process group
|
// run the command in its own process group
|
||||||
// this ensures that sending ctrl-c to restic will not immediately stop the backend process.
|
// this ensures that sending ctrl-c to restic will not immediately stop the backend process.
|
||||||
@@ -39,15 +29,15 @@ func startForeground(cmd *exec.Cmd) (bg func() error, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// only move child process to foreground if restic is in the foreground
|
// only move child process to foreground if restic is in the foreground
|
||||||
prev, err := tcgetpgrp(int(tty.Fd()))
|
prev, err := termstatus.Tcgetpgrp(int(tty.Fd()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tty.Close()
|
_ = tty.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
self := unix.Getpgrp()
|
self := termstatus.Getpgrp()
|
||||||
if prev != self {
|
if prev != self {
|
||||||
debug.Log("restic is not controlling the tty")
|
debug.Log("restic is not controlling the tty; err = %v", err)
|
||||||
if err := tty.Close(); err != nil {
|
if err := tty.Close(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -66,7 +56,7 @@ func startForeground(cmd *exec.Cmd) (bg func() error, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// move the command's process group into the foreground
|
// move the command's process group into the foreground
|
||||||
err = tcsetpgrp(int(tty.Fd()), cmd.Process.Pid)
|
err = termstatus.Tcsetpgrp(int(tty.Fd()), cmd.Process.Pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tty.Close()
|
_ = tty.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -77,7 +67,7 @@ func startForeground(cmd *exec.Cmd) (bg func() error, err error) {
|
|||||||
signal.Reset(unix.SIGTTOU)
|
signal.Reset(unix.SIGTTOU)
|
||||||
|
|
||||||
// reset the foreground process group
|
// reset the foreground process group
|
||||||
err = tcsetpgrp(int(tty.Fd()), prev)
|
err = termstatus.Tcsetpgrp(int(tty.Fd()), prev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tty.Close()
|
_ = tty.Close()
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
24
internal/ui/termstatus/background_unix.go
Normal file
24
internal/ui/termstatus/background_unix.go
Normal 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
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build unix
|
||||||
|
|
||||||
package termstatus
|
package termstatus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -13,7 +15,7 @@ func TestIsProcessBackground(t *testing.T) {
|
|||||||
t.Skipf("can't open terminal: %v", err)
|
t.Skipf("can't open terminal: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = isProcessBackground(tty.Fd())
|
_, err = isProcessBackground(int(tty.Fd()))
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
|
|
||||||
_ = tty.Close()
|
_ = tty.Close()
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
//go:build !linux
|
|
||||||
// +build !linux
|
|
||||||
|
|
||||||
package termstatus
|
package termstatus
|
||||||
|
|
||||||
// IsProcessBackground reports whether the current process is running in the
|
// IsProcessBackground reports whether the current process is running in the
|
||||||
8
internal/ui/termstatus/getpgrp_solaris.go
Normal file
8
internal/ui/termstatus/getpgrp_solaris.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package termstatus
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
func Getpgrp() int {
|
||||||
|
pid, _ := unix.Getpgrp()
|
||||||
|
return pid
|
||||||
|
}
|
||||||
7
internal/ui/termstatus/getpgrp_unix.go
Normal file
7
internal/ui/termstatus/getpgrp_unix.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
//go:build unix && !solaris
|
||||||
|
|
||||||
|
package termstatus
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
func Getpgrp() int { return unix.Getpgrp() }
|
||||||
12
internal/ui/termstatus/tcgetpgrp_linux.go
Normal file
12
internal/ui/termstatus/tcgetpgrp_linux.go
Normal 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
|
||||||
|
}
|
||||||
9
internal/ui/termstatus/tcgetpgrp_unix.go
Normal file
9
internal/ui/termstatus/tcgetpgrp_unix.go
Normal 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)
|
||||||
|
}
|
||||||
10
internal/ui/termstatus/tcsetpgrp_aix.go
Normal file
10
internal/ui/termstatus/tcsetpgrp_aix.go
Normal 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)
|
||||||
|
}
|
||||||
9
internal/ui/termstatus/tcsetpgrp_unix.go
Normal file
9
internal/ui/termstatus/tcsetpgrp_unix.go
Normal 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)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user