console_linux.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. package libcontainer
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "syscall"
  7. "unsafe"
  8. "github.com/opencontainers/runc/libcontainer/label"
  9. )
  10. // NewConsole returns an initalized console that can be used within a container by copying bytes
  11. // from the master side to the slave that is attached as the tty for the container's init process.
  12. func NewConsole(uid, gid int) (Console, error) {
  13. master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
  14. if err != nil {
  15. return nil, err
  16. }
  17. console, err := ptsname(master)
  18. if err != nil {
  19. return nil, err
  20. }
  21. if err := unlockpt(master); err != nil {
  22. return nil, err
  23. }
  24. if err := os.Chmod(console, 0600); err != nil {
  25. return nil, err
  26. }
  27. if err := os.Chown(console, uid, gid); err != nil {
  28. return nil, err
  29. }
  30. return &linuxConsole{
  31. slavePath: console,
  32. master: master,
  33. }, nil
  34. }
  35. // newConsoleFromPath is an internal function returning an initialized console for use inside
  36. // a container's MNT namespace.
  37. func newConsoleFromPath(slavePath string) *linuxConsole {
  38. return &linuxConsole{
  39. slavePath: slavePath,
  40. }
  41. }
  42. // linuxConsole is a linux psuedo TTY for use within a container.
  43. type linuxConsole struct {
  44. master *os.File
  45. slavePath string
  46. }
  47. func (c *linuxConsole) Fd() uintptr {
  48. return c.master.Fd()
  49. }
  50. func (c *linuxConsole) Path() string {
  51. return c.slavePath
  52. }
  53. func (c *linuxConsole) Read(b []byte) (int, error) {
  54. return c.master.Read(b)
  55. }
  56. func (c *linuxConsole) Write(b []byte) (int, error) {
  57. return c.master.Write(b)
  58. }
  59. func (c *linuxConsole) Close() error {
  60. if m := c.master; m != nil {
  61. return m.Close()
  62. }
  63. return nil
  64. }
  65. // mount initializes the console inside the rootfs mounting with the specified mount label
  66. // and applying the correct ownership of the console.
  67. func (c *linuxConsole) mount(rootfs, mountLabel string) error {
  68. oldMask := syscall.Umask(0000)
  69. defer syscall.Umask(oldMask)
  70. if err := label.SetFileLabel(c.slavePath, mountLabel); err != nil {
  71. return err
  72. }
  73. dest := filepath.Join(rootfs, "/dev/console")
  74. f, err := os.Create(dest)
  75. if err != nil && !os.IsExist(err) {
  76. return err
  77. }
  78. if f != nil {
  79. f.Close()
  80. }
  81. return syscall.Mount(c.slavePath, dest, "bind", syscall.MS_BIND, "")
  82. }
  83. // dupStdio opens the slavePath for the console and dups the fds to the current
  84. // processes stdio, fd 0,1,2.
  85. func (c *linuxConsole) dupStdio() error {
  86. slave, err := c.open(syscall.O_RDWR)
  87. if err != nil {
  88. return err
  89. }
  90. fd := int(slave.Fd())
  91. for _, i := range []int{0, 1, 2} {
  92. if err := syscall.Dup3(fd, i, 0); err != nil {
  93. return err
  94. }
  95. }
  96. return nil
  97. }
  98. // open is a clone of os.OpenFile without the O_CLOEXEC used to open the pty slave.
  99. func (c *linuxConsole) open(flag int) (*os.File, error) {
  100. r, e := syscall.Open(c.slavePath, flag, 0)
  101. if e != nil {
  102. return nil, &os.PathError{
  103. Op: "open",
  104. Path: c.slavePath,
  105. Err: e,
  106. }
  107. }
  108. return os.NewFile(uintptr(r), c.slavePath), nil
  109. }
  110. func ioctl(fd uintptr, flag, data uintptr) error {
  111. if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, flag, data); err != 0 {
  112. return err
  113. }
  114. return nil
  115. }
  116. // unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
  117. // unlockpt should be called before opening the slave side of a pty.
  118. func unlockpt(f *os.File) error {
  119. var u int32
  120. return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
  121. }
  122. // ptsname retrieves the name of the first available pts for the given master.
  123. func ptsname(f *os.File) (string, error) {
  124. var n int32
  125. if err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))); err != nil {
  126. return "", err
  127. }
  128. return fmt.Sprintf("/dev/pts/%d", n), nil
  129. }