Path: blob/main/components/common-go/process/process.go
2498 views
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package process56import (7"context"8"errors"9"os"1011"golang.org/x/sys/unix"12)1314// IsNotChildProcess checks if an error returned by a command15// execution is an error related to no child processes running16// This can be seen, for instance, in short lived commands.17func IsNotChildProcess(err error) bool {18if err == nil {19return false20}2122return (err.Error() == "wait: no child processes" || err.Error() == "waitid: no child processes")23}2425var ErrForceKilled = errors.New("Process didn't terminate, so we sent SIGKILL")2627// TerminateSync sends a SIGTERM to the given process and returns when the process has terminated or when the context was cancelled.28// When the context is cancelled this function sends a SIGKILL to the process and return immediately with ErrForceKilled.29func TerminateSync(ctx context.Context, pid int) error {30process, err := os.FindProcess(pid)31if err != nil { // never happens on UNIX32return err33}34err = process.Signal(unix.SIGTERM)35if err != nil {36if err == os.ErrProcessDone {37return nil38}39return err40}41terminated := make(chan error, 1)42go func() {43_, err := process.Wait()44terminated <- err45}()46select {47case err := <-terminated:48return err49case <-ctx.Done():50err = process.Kill()51if err != nil {52return err53}54return ErrForceKilled55}56}575859