github.com/erriapo/docker@v1.6.0-rc2/daemon/networkdriver/portmapper/proxy.go (about) 1 package portmapper 2 3 import ( 4 "flag" 5 "fmt" 6 "io/ioutil" 7 "log" 8 "net" 9 "os" 10 "os/exec" 11 "os/signal" 12 "strconv" 13 "syscall" 14 "time" 15 16 "github.com/docker/docker/pkg/proxy" 17 "github.com/docker/docker/pkg/reexec" 18 ) 19 20 const userlandProxyCommandName = "docker-proxy" 21 22 func init() { 23 reexec.Register(userlandProxyCommandName, execProxy) 24 } 25 26 type UserlandProxy interface { 27 Start() error 28 Stop() error 29 } 30 31 // proxyCommand wraps an exec.Cmd to run the userland TCP and UDP 32 // proxies as separate processes. 33 type proxyCommand struct { 34 cmd *exec.Cmd 35 } 36 37 // execProxy is the reexec function that is registered to start the userland proxies 38 func execProxy() { 39 f := os.NewFile(3, "signal-parent") 40 host, container := parseHostContainerAddrs() 41 42 p, err := proxy.NewProxy(host, container) 43 if err != nil { 44 fmt.Fprintf(f, "1\n%s", err) 45 f.Close() 46 os.Exit(1) 47 } 48 go handleStopSignals(p) 49 fmt.Fprint(f, "0\n") 50 f.Close() 51 52 // Run will block until the proxy stops 53 p.Run() 54 } 55 56 // parseHostContainerAddrs parses the flags passed on reexec to create the TCP or UDP 57 // net.Addrs to map the host and container ports 58 func parseHostContainerAddrs() (host net.Addr, container net.Addr) { 59 var ( 60 proto = flag.String("proto", "tcp", "proxy protocol") 61 hostIP = flag.String("host-ip", "", "host ip") 62 hostPort = flag.Int("host-port", -1, "host port") 63 containerIP = flag.String("container-ip", "", "container ip") 64 containerPort = flag.Int("container-port", -1, "container port") 65 ) 66 67 flag.Parse() 68 69 switch *proto { 70 case "tcp": 71 host = &net.TCPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort} 72 container = &net.TCPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort} 73 case "udp": 74 host = &net.UDPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort} 75 container = &net.UDPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort} 76 default: 77 log.Fatalf("unsupported protocol %s", *proto) 78 } 79 80 return host, container 81 } 82 83 func handleStopSignals(p proxy.Proxy) { 84 s := make(chan os.Signal, 10) 85 signal.Notify(s, os.Interrupt, syscall.SIGTERM, syscall.SIGSTOP) 86 87 for _ = range s { 88 p.Close() 89 90 os.Exit(0) 91 } 92 } 93 94 func NewProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net.IP, containerPort int) UserlandProxy { 95 args := []string{ 96 userlandProxyCommandName, 97 "-proto", proto, 98 "-host-ip", hostIP.String(), 99 "-host-port", strconv.Itoa(hostPort), 100 "-container-ip", containerIP.String(), 101 "-container-port", strconv.Itoa(containerPort), 102 } 103 104 return &proxyCommand{ 105 cmd: &exec.Cmd{ 106 Path: reexec.Self(), 107 Args: args, 108 SysProcAttr: &syscall.SysProcAttr{ 109 Pdeathsig: syscall.SIGTERM, // send a sigterm to the proxy if the daemon process dies 110 }, 111 }, 112 } 113 } 114 115 func (p *proxyCommand) Start() error { 116 r, w, err := os.Pipe() 117 if err != nil { 118 return fmt.Errorf("proxy unable to open os.Pipe %s", err) 119 } 120 defer r.Close() 121 p.cmd.ExtraFiles = []*os.File{w} 122 if err := p.cmd.Start(); err != nil { 123 return err 124 } 125 w.Close() 126 127 errchan := make(chan error, 1) 128 go func() { 129 buf := make([]byte, 2) 130 r.Read(buf) 131 132 if string(buf) != "0\n" { 133 errStr, err := ioutil.ReadAll(r) 134 if err != nil { 135 errchan <- fmt.Errorf("Error reading exit status from userland proxy: %v", err) 136 return 137 } 138 139 errchan <- fmt.Errorf("Error starting userland proxy: %s", errStr) 140 return 141 } 142 errchan <- nil 143 }() 144 145 select { 146 case err := <-errchan: 147 return err 148 case <-time.After(16 * time.Second): 149 return fmt.Errorf("Timed out proxy starting the userland proxy") 150 } 151 } 152 153 func (p *proxyCommand) Stop() error { 154 if p.cmd.Process != nil { 155 if err := p.cmd.Process.Signal(os.Interrupt); err != nil { 156 return err 157 } 158 return p.cmd.Wait() 159 } 160 return nil 161 }