You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

224 lines
6.1 KiB

  1. // +build !windows
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. // This code is heavily inspired by the archived gofacebook/gracenet/net.go handler
  6. package graceful
  7. import (
  8. "fmt"
  9. "net"
  10. "os"
  11. "strconv"
  12. "strings"
  13. "sync"
  14. "code.gitea.io/gitea/modules/log"
  15. "code.gitea.io/gitea/modules/setting"
  16. )
  17. const (
  18. listenFDs = "LISTEN_FDS"
  19. startFD = 3
  20. )
  21. // In order to keep the working directory the same as when we started we record
  22. // it at startup.
  23. var originalWD, _ = os.Getwd()
  24. var (
  25. once = sync.Once{}
  26. mutex = sync.Mutex{}
  27. providedListeners = []net.Listener{}
  28. activeListeners = []net.Listener{}
  29. )
  30. func getProvidedFDs() (savedErr error) {
  31. // Only inherit the provided FDS once but we will save the error so that repeated calls to this function will return the same error
  32. once.Do(func() {
  33. mutex.Lock()
  34. defer mutex.Unlock()
  35. numFDs := os.Getenv(listenFDs)
  36. if numFDs == "" {
  37. return
  38. }
  39. n, err := strconv.Atoi(numFDs)
  40. if err != nil {
  41. savedErr = fmt.Errorf("%s is not a number: %s. Err: %v", listenFDs, numFDs, err)
  42. return
  43. }
  44. for i := startFD; i < n+startFD; i++ {
  45. file := os.NewFile(uintptr(i), fmt.Sprintf("listener_FD%d", i))
  46. l, err := net.FileListener(file)
  47. if err == nil {
  48. // Close the inherited file if it's a listener
  49. if err = file.Close(); err != nil {
  50. savedErr = fmt.Errorf("error closing provided socket fd %d: %s", i, err)
  51. return
  52. }
  53. providedListeners = append(providedListeners, l)
  54. continue
  55. }
  56. // If needed we can handle packetconns here.
  57. savedErr = fmt.Errorf("Error getting provided socket fd %d: %v", i, err)
  58. return
  59. }
  60. })
  61. return savedErr
  62. }
  63. // CloseProvidedListeners closes all unused provided listeners.
  64. func CloseProvidedListeners() error {
  65. mutex.Lock()
  66. defer mutex.Unlock()
  67. var returnableError error
  68. for _, l := range providedListeners {
  69. err := l.Close()
  70. if err != nil {
  71. log.Error("Error in closing unused provided listener: %v", err)
  72. if returnableError != nil {
  73. returnableError = fmt.Errorf("%v & %v", returnableError, err)
  74. } else {
  75. returnableError = err
  76. }
  77. }
  78. }
  79. providedListeners = []net.Listener{}
  80. return returnableError
  81. }
  82. // GetListener obtains a listener for the local network address. The network must be
  83. // a stream-oriented network: "tcp", "tcp4", "tcp6", "unix" or "unixpacket". It
  84. // returns an provided net.Listener for the matching network and address, or
  85. // creates a new one using net.Listen.
  86. func GetListener(network, address string) (net.Listener, error) {
  87. // Add a deferral to say that we've tried to grab a listener
  88. defer GetManager().InformCleanup()
  89. switch network {
  90. case "tcp", "tcp4", "tcp6":
  91. tcpAddr, err := net.ResolveTCPAddr(network, address)
  92. if err != nil {
  93. return nil, err
  94. }
  95. return GetListenerTCP(network, tcpAddr)
  96. case "unix", "unixpacket":
  97. unixAddr, err := net.ResolveUnixAddr(network, address)
  98. if err != nil {
  99. return nil, err
  100. }
  101. return GetListenerUnix(network, unixAddr)
  102. default:
  103. return nil, net.UnknownNetworkError(network)
  104. }
  105. }
  106. // GetListenerTCP announces on the local network address. The network must be:
  107. // "tcp", "tcp4" or "tcp6". It returns a provided net.Listener for the
  108. // matching network and address, or creates a new one using net.ListenTCP.
  109. func GetListenerTCP(network string, address *net.TCPAddr) (*net.TCPListener, error) {
  110. if err := getProvidedFDs(); err != nil {
  111. return nil, err
  112. }
  113. mutex.Lock()
  114. defer mutex.Unlock()
  115. // look for a provided listener
  116. for i, l := range providedListeners {
  117. if isSameAddr(l.Addr(), address) {
  118. providedListeners = append(providedListeners[:i], providedListeners[i+1:]...)
  119. activeListeners = append(activeListeners, l)
  120. return l.(*net.TCPListener), nil
  121. }
  122. }
  123. // no provided listener for this address -> make a fresh listener
  124. l, err := net.ListenTCP(network, address)
  125. if err != nil {
  126. return nil, err
  127. }
  128. activeListeners = append(activeListeners, l)
  129. return l, nil
  130. }
  131. // GetListenerUnix announces on the local network address. The network must be:
  132. // "unix" or "unixpacket". It returns a provided net.Listener for the
  133. // matching network and address, or creates a new one using net.ListenUnix.
  134. func GetListenerUnix(network string, address *net.UnixAddr) (*net.UnixListener, error) {
  135. if err := getProvidedFDs(); err != nil {
  136. return nil, err
  137. }
  138. mutex.Lock()
  139. defer mutex.Unlock()
  140. // look for a provided listener
  141. for i, l := range providedListeners {
  142. if isSameAddr(l.Addr(), address) {
  143. providedListeners = append(providedListeners[:i], providedListeners[i+1:]...)
  144. activeListeners = append(activeListeners, l)
  145. unixListener := l.(*net.UnixListener)
  146. unixListener.SetUnlinkOnClose(true)
  147. return unixListener, nil
  148. }
  149. }
  150. // make a fresh listener
  151. if err := os.Remove(address.Name); err != nil && !os.IsNotExist(err) {
  152. return nil, fmt.Errorf("Failed to remove unix socket %s: %v", address.Name, err)
  153. }
  154. l, err := net.ListenUnix(network, address)
  155. if err != nil {
  156. return nil, err
  157. }
  158. fileMode := os.FileMode(setting.UnixSocketPermission)
  159. if err = os.Chmod(address.Name, fileMode); err != nil {
  160. return nil, fmt.Errorf("Failed to set permission of unix socket to %s: %v", fileMode.String(), err)
  161. }
  162. activeListeners = append(activeListeners, l)
  163. return l, nil
  164. }
  165. func isSameAddr(a1, a2 net.Addr) bool {
  166. // If the addresses are not on the same network fail.
  167. if a1.Network() != a2.Network() {
  168. return false
  169. }
  170. // If the two addresses have the same string representation they're equal
  171. a1s := a1.String()
  172. a2s := a2.String()
  173. if a1s == a2s {
  174. return true
  175. }
  176. // This allows for ipv6 vs ipv4 local addresses to compare as equal. This
  177. // scenario is common when listening on localhost.
  178. const ipv6prefix = "[::]"
  179. a1s = strings.TrimPrefix(a1s, ipv6prefix)
  180. a2s = strings.TrimPrefix(a2s, ipv6prefix)
  181. const ipv4prefix = "0.0.0.0"
  182. a1s = strings.TrimPrefix(a1s, ipv4prefix)
  183. a2s = strings.TrimPrefix(a2s, ipv4prefix)
  184. return a1s == a2s
  185. }
  186. func getActiveListeners() []net.Listener {
  187. mutex.Lock()
  188. defer mutex.Unlock()
  189. listeners := make([]net.Listener, len(activeListeners))
  190. copy(listeners, activeListeners)
  191. return listeners
  192. }