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 }