mirror of
https://github.com/restic/restic.git
synced 2025-10-27 11:24:17 +00:00
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.
88 lines
1.8 KiB
Go
88 lines
1.8 KiB
Go
//go:build unix
|
|
|
|
package util
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"os/signal"
|
|
|
|
"github.com/restic/restic/internal/debug"
|
|
"github.com/restic/restic/internal/errors"
|
|
"github.com/restic/restic/internal/ui/termstatus"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
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)
|
|
return startFallback(cmd)
|
|
}
|
|
|
|
// only move child process to foreground if restic is in the foreground
|
|
prev, err := termstatus.Tcgetpgrp(int(tty.Fd()))
|
|
if err != nil {
|
|
_ = tty.Close()
|
|
return nil, err
|
|
}
|
|
|
|
self := termstatus.Getpgrp()
|
|
if prev != self {
|
|
debug.Log("restic is not controlling the tty; err = %v", err)
|
|
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)
|
|
|
|
// start the process
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
_ = tty.Close()
|
|
return nil, errors.Wrap(err, "cmd.Start")
|
|
}
|
|
|
|
// move the command's process group into the foreground
|
|
err = termstatus.Tcsetpgrp(int(tty.Fd()), cmd.Process.Pid)
|
|
if err != nil {
|
|
_ = tty.Close()
|
|
return nil, err
|
|
}
|
|
|
|
bg = func() error {
|
|
signal.Reset(unix.SIGTTIN)
|
|
signal.Reset(unix.SIGTTOU)
|
|
|
|
// reset the foreground process group
|
|
err = termstatus.Tcsetpgrp(int(tty.Fd()), prev)
|
|
if err != nil {
|
|
_ = tty.Close()
|
|
return err
|
|
}
|
|
|
|
return tty.Close()
|
|
}
|
|
|
|
return bg, nil
|
|
}
|
|
|
|
func startFallback(cmd *exec.Cmd) (bg func() error, err error) {
|
|
bg = func() error {
|
|
return nil
|
|
}
|
|
|
|
return bg, cmd.Start()
|
|
}
|