From 4d5ae61d575c9c754737c26e62480c45a1250789 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Fri, 18 Oct 2024 09:49:51 -0400 Subject: [PATCH] innermost child --- .github/workflows/ci.yml | 3 ++ cargo-generate.toml | 2 +- main.go | 66 ++++++++++++++++++++++++++++++++++------ 3 files changed, 61 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 30a64f3..a6c019e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -104,3 +104,6 @@ jobs: with: go-version: stable - run: go run main.go ${{ github.workspace }} ${{ matrix.program }} + + - uses: mxschmitt/action-tmate@v3 + if: ${{ failure() }} 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..2edb5c2 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) } } @@ -170,11 +170,6 @@ func run() error { cmd := exec.CommandContext(ctx, "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,19 @@ 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 { + if _, err := fmt.Fprintf(os.Stderr, "failed to interrupt %d: %s", process, err); err != nil { + panic(err) + } + } + } } } if err := scanner.Err(); err != nil { @@ -207,3 +213,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) { + 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 + } + } + } + } + if !yield(process, nil) { + return + } + } +}