github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/hooks/exec/exec.go (about)

     1  // Package exec provides utilities for executing Open Container Initative runtime hooks.
     2  package exec
     3  
     4  import (
     5  	"bytes"
     6  	"context"
     7  	"fmt"
     8  	"io"
     9  	osexec "os/exec"
    10  	"time"
    11  
    12  	rspec "github.com/opencontainers/runtime-spec/specs-go"
    13  	"github.com/pkg/errors"
    14  	"github.com/sirupsen/logrus"
    15  )
    16  
    17  // DefaultPostKillTimeout is the recommended default post-kill timeout.
    18  const DefaultPostKillTimeout = time.Duration(10) * time.Second
    19  
    20  // Run executes the hook and waits for it to complete or for the
    21  // context or hook-specified timeout to expire.
    22  func Run(ctx context.Context, hook *rspec.Hook, state []byte, stdout io.Writer, stderr io.Writer, postKillTimeout time.Duration) (hookErr, err error) {
    23  	cmd := osexec.Cmd{
    24  		Path:   hook.Path,
    25  		Args:   hook.Args,
    26  		Env:    hook.Env,
    27  		Stdin:  bytes.NewReader(state),
    28  		Stdout: stdout,
    29  		Stderr: stderr,
    30  	}
    31  	if cmd.Env == nil {
    32  		cmd.Env = []string{}
    33  	}
    34  
    35  	if hook.Timeout != nil {
    36  		var cancel context.CancelFunc
    37  		ctx, cancel = context.WithTimeout(ctx, time.Duration(*hook.Timeout)*time.Second)
    38  		defer cancel()
    39  	}
    40  
    41  	err = cmd.Start()
    42  	if err != nil {
    43  		return err, err
    44  	}
    45  	exit := make(chan error, 1)
    46  	go func() {
    47  		err := cmd.Wait()
    48  		if err != nil {
    49  			err = errors.Wrapf(err, "executing %v", cmd.Args)
    50  		}
    51  		exit <- err
    52  	}()
    53  
    54  	select {
    55  	case err = <-exit:
    56  		return err, err
    57  	case <-ctx.Done():
    58  		if err := cmd.Process.Kill(); err != nil {
    59  			logrus.Errorf("failed to kill pid %v", cmd.Process)
    60  		}
    61  		timer := time.NewTimer(postKillTimeout)
    62  		defer timer.Stop()
    63  		select {
    64  		case <-timer.C:
    65  			err = fmt.Errorf("failed to reap process within %s of the kill signal", postKillTimeout)
    66  		case err = <-exit:
    67  		}
    68  		return err, ctx.Err()
    69  	}
    70  }