exec.go
⎇
Raw
1package util
2
3import (
4 "context"
5 "os/exec"
6 "syscall"
7 "time"
8)
9
10const killWaitDelay = time.Second
11
12// KillableCommand builds a command whose cancellation actually stops it, and is
13// the single place that plumbing lives — every context-aware external command
14// goes through here.
15//
16// yt-dlp, ffmpeg and ffprobe spawn helpers that inherit the output pipe, and
17// both Wait and Output block reading it until every holder exits. So killing
18// only the direct child does not end the call; the group signal does, with
19// WaitDelay as the backstop for whatever survives it.
20func KillableCommand(ctx context.Context, path string, args ...string) *exec.Cmd {
21 cmd := exec.CommandContext(ctx, path, args...)
22 cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
23 cmd.Cancel = func() error {
24 return syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
25 }
26 cmd.WaitDelay = killWaitDelay
27 return cmd
28}
29