github.com/mckael/restic@v0.8.3/internal/backend/sftp/foreground_unix.go (about)

     1  // +build !windows
     2  
     3  package sftp
     4  
     5  import (
     6  	"os"
     7  	"os/exec"
     8  	"os/signal"
     9  	"syscall"
    10  	"unsafe"
    11  
    12  	"github.com/restic/restic/internal/debug"
    13  	"github.com/restic/restic/internal/errors"
    14  )
    15  
    16  func tcsetpgrp(fd int, pid int) error {
    17  	_, _, errno := syscall.RawSyscall(syscall.SYS_IOCTL, uintptr(fd),
    18  		uintptr(syscall.TIOCSPGRP), uintptr(unsafe.Pointer(&pid)))
    19  	if errno == 0 {
    20  		return nil
    21  	}
    22  
    23  	return errno
    24  }
    25  
    26  // startForeground runs cmd in the foreground, by temporarily switching to the
    27  // new process group created for cmd. The returned function `bg` switches back
    28  // to the previous process group.
    29  func startForeground(cmd *exec.Cmd) (bg func() error, err error) {
    30  	// open the TTY, we need the file descriptor
    31  	tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
    32  	if err != nil {
    33  		debug.Log("unable to open tty: %v", err)
    34  		bg = func() error {
    35  			return nil
    36  		}
    37  		return bg, cmd.Start()
    38  	}
    39  
    40  	signal.Ignore(syscall.SIGTTIN)
    41  	signal.Ignore(syscall.SIGTTOU)
    42  
    43  	// run the command in its own process group
    44  	cmd.SysProcAttr = &syscall.SysProcAttr{
    45  		Setpgid: true,
    46  	}
    47  
    48  	// start the process
    49  	err = cmd.Start()
    50  	if err != nil {
    51  		_ = tty.Close()
    52  		return nil, errors.Wrap(err, "cmd.Start")
    53  	}
    54  
    55  	// move the command's process group into the foreground
    56  	prev := syscall.Getpgrp()
    57  	err = tcsetpgrp(int(tty.Fd()), cmd.Process.Pid)
    58  	if err != nil {
    59  		_ = tty.Close()
    60  		return nil, err
    61  	}
    62  
    63  	bg = func() error {
    64  		signal.Reset(syscall.SIGTTIN)
    65  		signal.Reset(syscall.SIGTTOU)
    66  
    67  		// reset the foreground process group
    68  		err = tcsetpgrp(int(tty.Fd()), prev)
    69  		if err != nil {
    70  			_ = tty.Close()
    71  			return err
    72  		}
    73  
    74  		return tty.Close()
    75  	}
    76  
    77  	return bg, nil
    78  }