backend: only move child process to foreground if already in foreground

The rclone and sftp backends require starting a child process. The are
first moved into the foreground and back into the background after the
initial startup is done.

However, this behavior is also active if restic itself is started in the
background. In this case, restic changing the foreground process may
confuse the shell and in case of bash causes it to exit. Thus, disable
modification of the controlling process group of the tty if restic is
run in the background.
This commit is contained in:
Michael Eischer
2025-04-11 23:54:02 +02:00
parent 2ad703bfd8
commit 4c19d6410f

View File

@@ -14,6 +14,10 @@ import (
"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.
@@ -25,12 +29,26 @@ func startForeground(cmd *exec.Cmd) (bg func() error, err error) {
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
debug.Log("unable to open tty: %v", err)
bg = func() error {
return nil
}
return bg, cmd.Start()
return startFallback(cmd)
}
// only move child process to foreground if restic is in the foreground
prev, err := tcgetpgrp(int(tty.Fd()))
if err != nil {
_ = tty.Close()
return nil, err
}
self := unix.Getpgrp()
if prev != self {
debug.Log("restic is not controlling the tty")
if err := tty.Close(); err != nil {
return nil, err
}
return startFallback(cmd)
}
// Prevent getting suspended when interacting with the tty
signal.Ignore(unix.SIGTTIN)
signal.Ignore(unix.SIGTTOU)
@@ -47,7 +65,6 @@ func startForeground(cmd *exec.Cmd) (bg func() error, err error) {
}
// move the command's process group into the foreground
prev := unix.Getpgrp()
err = tcsetpgrp(int(tty.Fd()), cmd.Process.Pid)
if err != nil {
_ = tty.Close()
@@ -70,3 +87,10 @@ func startForeground(cmd *exec.Cmd) (bg func() error, err error) {
return bg, nil
}
func startFallback(cmd *exec.Cmd) (bg func() error, err error) {
bg = func() error {
return nil
}
return bg, cmd.Start()
}