From cbb8872972bf031da5d3e43c479317b914baa6bf Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Fri, 18 Oct 2024 09:49:51 -0400 Subject: [PATCH] innermost child --- cargo-generate.toml | 2 +- main.go | 66 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/cargo-generate.toml b/cargo-generate.toml index efa3ccc..8dcf2a3 100644 --- a/cargo-generate.toml +++ b/cargo-generate.toml @@ -1,6 +1,6 @@ [template] cargo_generate_version = ">=0.10.0" -ignore = [".github", "test.py"] +ignore = [".github", "main.go"] [placeholders.program_type] type = "string" diff --git a/main.go b/main.go index 3578a6f..fbdcc81 100644 --- a/main.go +++ b/main.go @@ -8,8 +8,8 @@ import ( "os/exec" "path/filepath" "runtime" + "strconv" "strings" - "syscall" "time" ) @@ -157,9 +157,9 @@ func run() error { cmd.Env = cmdSpec.Env cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - fmt.Printf("%+v\n", cmdSpec) + fmt.Printf("%#v\n", cmdSpec) if err := cmd.Run(); err != nil { - return fmt.Errorf("%+v failed: %w", cmdSpec, err) + return fmt.Errorf("%#v failed: %w", cmdSpec, err) } } @@ -167,14 +167,9 @@ func run() error { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() - cmd := exec.CommandContext(ctx, "cargo", "xtask", "run") + cmd := exec.CommandContext(ctx, "sh", "-c", "cargo xtask run") cmd.Dir = projectDir cmd.Stderr = os.Stderr - // Prevent the child process from being in our process group so that we can send it a SIGINT - // without sending one to ourselves. - cmd.SysProcAttr = &syscall.SysProcAttr{ - Setpgid: true, - } stdoutPipe, err := cmd.StdoutPipe() if err != nil { @@ -192,8 +187,17 @@ func run() error { if _, err := fmt.Fprintln(os.Stdout, text); err != nil { panic(err) } + if strings.Contains(text, "Waiting for Ctrl-C") { - syscall.Kill(-cmd.Process.Pid, syscall.SIGINT) + for process, err := range children(cmd.Process) { + if err != nil { + return err + } + fmt.Printf("Sending SIGINT to child pid %d\n", process.Pid) + if err := process.Signal(os.Interrupt); err != nil { + return fmt.Errorf("failed to interrupt %d: %w", process, err) + } + } } } if err := scanner.Err(); err != nil { @@ -207,3 +211,45 @@ func run() error { return nil } + +func children(process *os.Process) func(func(*os.Process, error) bool) { + return func(yield func(*os.Process, error) bool) { + if !yield(process, nil) { + return + } + taskPath := fmt.Sprintf("/proc/%d/task", process.Pid) + entries, err := os.ReadDir(taskPath) + if err != nil { + yield(nil, fmt.Errorf("failed to readdir %s: %w", taskPath, err)) + return + } + for _, entry := range entries { + if !entry.IsDir() { + continue + } + childrenPath := fmt.Sprintf("%s/%s/children", taskPath, entry.Name()) + childrenContent, err := os.ReadFile(childrenPath) + if err != nil { + yield(nil, fmt.Errorf("failed to read %s: %w", childrenPath, err)) + return + } + for _, childField := range strings.Fields(string(childrenContent)) { + childPid, err := strconv.Atoi(childField) + if err != nil { + yield(nil, fmt.Errorf("failed to parse %s: %w", childField, err)) + return + } + child, err := os.FindProcess(childPid) + if err != nil { + yield(nil, fmt.Errorf("failed to find process %d: %w", childPid, err)) + return + } + for process, err := range children(child) { + if !yield(process, err) { + return + } + } + } + } + } +}