diff --git a/changelog/unreleased/issue-5354 b/changelog/unreleased/issue-5354 new file mode 100644 index 000000000..1fbda9077 --- /dev/null +++ b/changelog/unreleased/issue-5354 @@ -0,0 +1,12 @@ +Bugfix: Allow use of rclone/sftp backend when running restic in background + +When starting restic in the background, this could result in unexpected behavior +when using the rclone or sftp backend. + +For example running `restic -r rclone:./example --insecure-no-password init &` +could cause the calling `bash` shell to exit unexpectedly. + +This has been fixed. + +https://github.com/restic/restic/issues/5354 +https://github.com/restic/restic/pull/5358 diff --git a/internal/backend/util/foreground_unix.go b/internal/backend/util/foreground_unix.go index 082b7f59b..cba22ab26 100644 --- a/internal/backend/util/foreground_unix.go +++ b/internal/backend/util/foreground_unix.go @@ -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. @@ -21,24 +25,39 @@ func tcsetpgrp(fd int, pid int) error { } func startForeground(cmd *exec.Cmd) (bg func() error, err error) { + // run the command in its own process group + // this ensures that sending ctrl-c to restic will not immediately stop the backend process. + cmd.SysProcAttr = &unix.SysProcAttr{ + Setpgid: true, + } + // open the TTY, we need the file descriptor 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) - // run the command in its own process group - cmd.SysProcAttr = &unix.SysProcAttr{ - Setpgid: true, - } - // start the process err = cmd.Start() if err != nil { @@ -47,7 +66,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 +88,11 @@ 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() +}