console.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. //go:build !windows
  2. /*
  3. Copyright The containerd 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 runc
  15. import (
  16. "fmt"
  17. "net"
  18. "os"
  19. "path/filepath"
  20. "github.com/containerd/console"
  21. "golang.org/x/sys/unix"
  22. )
  23. // NewConsoleSocket creates a new unix socket at the provided path to accept a
  24. // pty master created by runc for use by the container
  25. func NewConsoleSocket(path string) (*Socket, error) {
  26. abs, err := filepath.Abs(path)
  27. if err != nil {
  28. return nil, err
  29. }
  30. addr, err := net.ResolveUnixAddr("unix", abs)
  31. if err != nil {
  32. return nil, err
  33. }
  34. l, err := net.ListenUnix("unix", addr)
  35. if err != nil {
  36. return nil, err
  37. }
  38. return &Socket{
  39. l: l,
  40. }, nil
  41. }
  42. // NewTempConsoleSocket returns a temp console socket for use with a container
  43. // On Close(), the socket is deleted
  44. func NewTempConsoleSocket() (*Socket, error) {
  45. runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
  46. dir, err := os.MkdirTemp(runtimeDir, "pty")
  47. if err != nil {
  48. return nil, err
  49. }
  50. abs, err := filepath.Abs(filepath.Join(dir, "pty.sock"))
  51. if err != nil {
  52. return nil, err
  53. }
  54. addr, err := net.ResolveUnixAddr("unix", abs)
  55. if err != nil {
  56. return nil, err
  57. }
  58. l, err := net.ListenUnix("unix", addr)
  59. if err != nil {
  60. return nil, err
  61. }
  62. if runtimeDir != "" {
  63. if err := os.Chmod(abs, 0o755|os.ModeSticky); err != nil {
  64. return nil, err
  65. }
  66. }
  67. return &Socket{
  68. l: l,
  69. rmdir: true,
  70. }, nil
  71. }
  72. // Socket is a unix socket that accepts the pty master created by runc
  73. type Socket struct {
  74. rmdir bool
  75. l *net.UnixListener
  76. }
  77. // Path returns the path to the unix socket on disk
  78. func (c *Socket) Path() string {
  79. return c.l.Addr().String()
  80. }
  81. // recvFd waits for a file descriptor to be sent over the given AF_UNIX
  82. // socket. The file name of the remote file descriptor will be recreated
  83. // locally (it is sent as non-auxiliary data in the same payload).
  84. func recvFd(socket *net.UnixConn) (*os.File, error) {
  85. const MaxNameLen = 4096
  86. oobSpace := unix.CmsgSpace(4)
  87. name := make([]byte, MaxNameLen)
  88. oob := make([]byte, oobSpace)
  89. n, oobn, _, _, err := socket.ReadMsgUnix(name, oob)
  90. if err != nil {
  91. return nil, err
  92. }
  93. if n >= MaxNameLen || oobn != oobSpace {
  94. return nil, fmt.Errorf("recvfd: incorrect number of bytes read (n=%d oobn=%d)", n, oobn)
  95. }
  96. // Truncate.
  97. name = name[:n]
  98. oob = oob[:oobn]
  99. scms, err := unix.ParseSocketControlMessage(oob)
  100. if err != nil {
  101. return nil, err
  102. }
  103. if len(scms) != 1 {
  104. return nil, fmt.Errorf("recvfd: number of SCMs is not 1: %d", len(scms))
  105. }
  106. scm := scms[0]
  107. fds, err := unix.ParseUnixRights(&scm)
  108. if err != nil {
  109. return nil, err
  110. }
  111. if len(fds) != 1 {
  112. return nil, fmt.Errorf("recvfd: number of fds is not 1: %d", len(fds))
  113. }
  114. fd := uintptr(fds[0])
  115. return os.NewFile(fd, string(name)), nil
  116. }
  117. // ReceiveMaster blocks until the socket receives the pty master
  118. func (c *Socket) ReceiveMaster() (console.Console, error) {
  119. conn, err := c.l.Accept()
  120. if err != nil {
  121. return nil, err
  122. }
  123. defer conn.Close()
  124. uc, ok := conn.(*net.UnixConn)
  125. if !ok {
  126. return nil, fmt.Errorf("received connection which was not a unix socket")
  127. }
  128. f, err := recvFd(uc)
  129. if err != nil {
  130. return nil, err
  131. }
  132. return console.ConsoleFromFile(f)
  133. }
  134. // Close closes the unix socket
  135. func (c *Socket) Close() error {
  136. err := c.l.Close()
  137. if c.rmdir {
  138. if rerr := os.RemoveAll(filepath.Dir(c.Path())); err == nil {
  139. err = rerr
  140. }
  141. }
  142. return err
  143. }