github.com/adityamillind98/moby@v23.0.0-rc.4+incompatible/libnetwork/portmapper/proxy_linux.go (about)

     1  package portmapper
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net"
     7  	"os"
     8  	"os/exec"
     9  	"strconv"
    10  	"syscall"
    11  	"time"
    12  )
    13  
    14  const userlandProxyCommandName = "docker-proxy"
    15  
    16  func newProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net.IP, containerPort int, proxyPath string) (userlandProxy, error) {
    17  	path := proxyPath
    18  	if proxyPath == "" {
    19  		cmd, err := exec.LookPath(userlandProxyCommandName)
    20  		if err != nil {
    21  			return nil, err
    22  		}
    23  		path = cmd
    24  	}
    25  
    26  	args := []string{
    27  		path,
    28  		"-proto", proto,
    29  		"-host-ip", hostIP.String(),
    30  		"-host-port", strconv.Itoa(hostPort),
    31  		"-container-ip", containerIP.String(),
    32  		"-container-port", strconv.Itoa(containerPort),
    33  	}
    34  
    35  	return &proxyCommand{
    36  		cmd: &exec.Cmd{
    37  			Path: path,
    38  			Args: args,
    39  			SysProcAttr: &syscall.SysProcAttr{
    40  				Pdeathsig: syscall.SIGTERM, // send a sigterm to the proxy if the daemon process dies
    41  			},
    42  		},
    43  	}, nil
    44  }
    45  
    46  // proxyCommand wraps an exec.Cmd to run the userland TCP and UDP
    47  // proxies as separate processes.
    48  type proxyCommand struct {
    49  	cmd *exec.Cmd
    50  }
    51  
    52  func (p *proxyCommand) Start() error {
    53  	r, w, err := os.Pipe()
    54  	if err != nil {
    55  		return fmt.Errorf("proxy unable to open os.Pipe %s", err)
    56  	}
    57  	defer r.Close()
    58  	p.cmd.ExtraFiles = []*os.File{w}
    59  	if err := p.cmd.Start(); err != nil {
    60  		return err
    61  	}
    62  	w.Close()
    63  
    64  	errchan := make(chan error, 1)
    65  	go func() {
    66  		buf := make([]byte, 2)
    67  		r.Read(buf)
    68  
    69  		if string(buf) != "0\n" {
    70  			errStr, err := io.ReadAll(r)
    71  			if err != nil {
    72  				errchan <- fmt.Errorf("Error reading exit status from userland proxy: %v", err)
    73  				return
    74  			}
    75  
    76  			errchan <- fmt.Errorf("Error starting userland proxy: %s", errStr)
    77  			return
    78  		}
    79  		errchan <- nil
    80  	}()
    81  
    82  	select {
    83  	case err := <-errchan:
    84  		return err
    85  	case <-time.After(16 * time.Second):
    86  		return fmt.Errorf("Timed out proxy starting the userland proxy")
    87  	}
    88  }
    89  
    90  func (p *proxyCommand) Stop() error {
    91  	if p.cmd.Process != nil {
    92  		if err := p.cmd.Process.Signal(os.Interrupt); err != nil {
    93  			return err
    94  		}
    95  		return p.cmd.Wait()
    96  	}
    97  	return nil
    98  }