package git import ( "context" "os/exec" "runtime" "syscall" "time" "golang.org/x/sys/unix" ) func runWithStandardUmask(ctx context.Context, cmd *exec.Cmd) error { errCh := make(chan error) go func() { defer close(errCh) runtime.LockOSThread() if err := unshareAndRun(ctx, cmd); err != nil { errCh <- err } }() return <-errCh } // unshareAndRun needs to be called in a locked thread. func unshareAndRun(ctx context.Context, cmd *exec.Cmd) error { if err := syscall.Unshare(syscall.CLONE_FS); err != nil { return err } syscall.Umask(0022) return runProcessGroup(ctx, cmd) } func runProcessGroup(ctx context.Context, cmd *exec.Cmd) error { cmd.SysProcAttr = &unix.SysProcAttr{ Setpgid: true, Pdeathsig: unix.SIGTERM, } if err := cmd.Start(); err != nil { return err } waitDone := make(chan struct{}) go func() { select { case <-ctx.Done(): _ = unix.Kill(-cmd.Process.Pid, unix.SIGTERM) go func() { select { case <-waitDone: case <-time.After(10 * time.Second): _ = unix.Kill(-cmd.Process.Pid, unix.SIGKILL) } }() case <-waitDone: } }() err := cmd.Wait() close(waitDone) return err }