github.com/telepresenceio/telepresence/v2@v2.20.0-pro.6.0.20240517030216-236ea954e789/pkg/dpipe/dpipe.go (about)

     1  package dpipe
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"os/exec" //nolint:depguard // We want no logging and no soft-context signal handling
     8  
     9  	"github.com/datawire/dlib/dlog"
    10  	"github.com/telepresenceio/telepresence/v2/pkg/shellquote"
    11  )
    12  
    13  func DPipe(ctx context.Context, peer io.ReadWriteCloser, cmdName string, cmdArgs ...string) error {
    14  	defer func() {
    15  		_ = peer.Close()
    16  	}()
    17  
    18  	cmd := exec.CommandContext(ctx, cmdName, cmdArgs...)
    19  	cmd.Stdin = peer
    20  	cmd.Stdout = peer
    21  	cmd.Stderr = dlog.StdLogger(ctx, dlog.LogLevelError).Writer()
    22  
    23  	cmdLine := shellquote.ShellString(cmd.Path, cmd.Args)
    24  	if err := cmd.Start(); err != nil {
    25  		return fmt.Errorf("failed to start %s: %w", cmdLine, err)
    26  	}
    27  
    28  	ctx = dlog.WithField(ctx, "exec.pid", cmd.Process.Pid)
    29  	dlog.Infof(ctx, "started command %s", cmdLine)
    30  	defer dlog.Infof(ctx, "ended command %s", cmdName)
    31  	runFinished := make(chan error)
    32  	go func() {
    33  		defer close(runFinished)
    34  		if err := cmd.Wait(); err != nil {
    35  			if !cmd.ProcessState.Success() && ctx.Err() == nil {
    36  				runFinished <- err
    37  			}
    38  		}
    39  	}()
    40  
    41  	select {
    42  	case <-ctx.Done():
    43  		killProcess(ctx, cmd)
    44  		return nil
    45  	case err := <-runFinished:
    46  		return err
    47  	}
    48  }