util_unix.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. // +build freebsd linux darwin
  2. /*
  3. Copyright 2017 The Kubernetes Authors.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. package util
  15. import (
  16. "context"
  17. "fmt"
  18. "io/ioutil"
  19. "net"
  20. "net/url"
  21. "os"
  22. "path/filepath"
  23. "golang.org/x/sys/unix"
  24. "k8s.io/klog"
  25. )
  26. const (
  27. // unixProtocol is the network protocol of unix socket.
  28. unixProtocol = "unix"
  29. )
  30. // CreateListener creates a listener on the specified endpoint.
  31. func CreateListener(endpoint string) (net.Listener, error) {
  32. protocol, addr, err := parseEndpointWithFallbackProtocol(endpoint, unixProtocol)
  33. if err != nil {
  34. return nil, err
  35. }
  36. if protocol != unixProtocol {
  37. return nil, fmt.Errorf("only support unix socket endpoint")
  38. }
  39. // Unlink to cleanup the previous socket file.
  40. err = unix.Unlink(addr)
  41. if err != nil && !os.IsNotExist(err) {
  42. return nil, fmt.Errorf("failed to unlink socket file %q: %v", addr, err)
  43. }
  44. if err := os.MkdirAll(filepath.Dir(addr), 0750); err != nil {
  45. return nil, fmt.Errorf("error creating socket directory %q: %v", filepath.Dir(addr), err)
  46. }
  47. // Create the socket on a tempfile and move it to the destination socket to handle improprer cleanup
  48. file, err := ioutil.TempFile(filepath.Dir(addr), "")
  49. if err != nil {
  50. return nil, fmt.Errorf("failed to create temporary file: %v", err)
  51. }
  52. if err := os.Remove(file.Name()); err != nil {
  53. return nil, fmt.Errorf("failed to remove temporary file: %v", err)
  54. }
  55. l, err := net.Listen(protocol, file.Name())
  56. if err != nil {
  57. return nil, err
  58. }
  59. if err = os.Rename(file.Name(), addr); err != nil {
  60. return nil, fmt.Errorf("failed to move temporary file to addr %q: %v", addr, err)
  61. }
  62. return l, nil
  63. }
  64. // GetAddressAndDialer returns the address parsed from the given endpoint and a context dialer.
  65. func GetAddressAndDialer(endpoint string) (string, func(ctx context.Context, addr string) (net.Conn, error), error) {
  66. protocol, addr, err := parseEndpointWithFallbackProtocol(endpoint, unixProtocol)
  67. if err != nil {
  68. return "", nil, err
  69. }
  70. if protocol != unixProtocol {
  71. return "", nil, fmt.Errorf("only support unix socket endpoint")
  72. }
  73. return addr, dial, nil
  74. }
  75. func dial(ctx context.Context, addr string) (net.Conn, error) {
  76. return (&net.Dialer{}).DialContext(ctx, unixProtocol, addr)
  77. }
  78. func parseEndpointWithFallbackProtocol(endpoint string, fallbackProtocol string) (protocol string, addr string, err error) {
  79. if protocol, addr, err = parseEndpoint(endpoint); err != nil && protocol == "" {
  80. fallbackEndpoint := fallbackProtocol + "://" + endpoint
  81. protocol, addr, err = parseEndpoint(fallbackEndpoint)
  82. if err == nil {
  83. klog.Warningf("Using %q as endpoint is deprecated, please consider using full url format %q.", endpoint, fallbackEndpoint)
  84. }
  85. }
  86. return
  87. }
  88. func parseEndpoint(endpoint string) (string, string, error) {
  89. u, err := url.Parse(endpoint)
  90. if err != nil {
  91. return "", "", err
  92. }
  93. switch u.Scheme {
  94. case "tcp":
  95. return "tcp", u.Host, nil
  96. case "unix":
  97. return "unix", u.Path, nil
  98. case "":
  99. return "", "", fmt.Errorf("using %q as endpoint is deprecated, please consider using full url format", endpoint)
  100. default:
  101. return u.Scheme, "", fmt.Errorf("protocol %q not supported", u.Scheme)
  102. }
  103. }
  104. // LocalEndpoint returns the full path to a unix socket at the given endpoint
  105. func LocalEndpoint(path, file string) (string, error) {
  106. u := url.URL{
  107. Scheme: unixProtocol,
  108. Path: path,
  109. }
  110. return filepath.Join(u.String(), file+".sock"), nil
  111. }
  112. // IsUnixDomainSocket returns whether a given file is a AF_UNIX socket file
  113. func IsUnixDomainSocket(filePath string) (bool, error) {
  114. fi, err := os.Stat(filePath)
  115. if err != nil {
  116. return false, fmt.Errorf("stat file %s failed: %v", filePath, err)
  117. }
  118. if fi.Mode()&os.ModeSocket == 0 {
  119. return false, nil
  120. }
  121. return true, nil
  122. }
  123. // NormalizePath is a no-op for Linux for now
  124. func NormalizePath(path string) string {
  125. return path
  126. }