proxy.go 3.6 KB

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