github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/runc/libcontainer/console_linux.go (about) 1 package libcontainer 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "syscall" 8 "unsafe" 9 10 "github.com/opencontainers/runc/libcontainer/label" 11 ) 12 13 // NewConsole returns an initialized console that can be used within a container by copying bytes 14 // from the master side to the slave that is attached as the tty for the container's init process. 15 func NewConsole(uid, gid int) (Console, error) { 16 master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0) 17 if err != nil { 18 return nil, err 19 } 20 if err := saneTerminal(master); err != nil { 21 return nil, err 22 } 23 console, err := ptsname(master) 24 if err != nil { 25 return nil, err 26 } 27 if err := unlockpt(master); err != nil { 28 return nil, err 29 } 30 if err := os.Chmod(console, 0600); err != nil { 31 return nil, err 32 } 33 if err := os.Chown(console, uid, gid); err != nil { 34 return nil, err 35 } 36 return &linuxConsole{ 37 slavePath: console, 38 master: master, 39 }, nil 40 } 41 42 // newConsoleFromPath is an internal function returning an initialized console for use inside 43 // a container's MNT namespace. 44 func newConsoleFromPath(slavePath string) *linuxConsole { 45 return &linuxConsole{ 46 slavePath: slavePath, 47 } 48 } 49 50 // linuxConsole is a linux pseudo TTY for use within a container. 51 type linuxConsole struct { 52 master *os.File 53 slavePath string 54 } 55 56 func (c *linuxConsole) Fd() uintptr { 57 return c.master.Fd() 58 } 59 60 func (c *linuxConsole) Path() string { 61 return c.slavePath 62 } 63 64 func (c *linuxConsole) Read(b []byte) (int, error) { 65 return c.master.Read(b) 66 } 67 68 func (c *linuxConsole) Write(b []byte) (int, error) { 69 return c.master.Write(b) 70 } 71 72 func (c *linuxConsole) Close() error { 73 if m := c.master; m != nil { 74 return m.Close() 75 } 76 return nil 77 } 78 79 // mount initializes the console inside the rootfs mounting with the specified mount label 80 // and applying the correct ownership of the console. 81 func (c *linuxConsole) mount(rootfs, mountLabel string) error { 82 oldMask := syscall.Umask(0000) 83 defer syscall.Umask(oldMask) 84 if err := label.SetFileLabel(c.slavePath, mountLabel); err != nil { 85 return err 86 } 87 dest := filepath.Join(rootfs, "/dev/console") 88 f, err := os.Create(dest) 89 if err != nil && !os.IsExist(err) { 90 return err 91 } 92 if f != nil { 93 f.Close() 94 } 95 return syscall.Mount(c.slavePath, dest, "bind", syscall.MS_BIND, "") 96 } 97 98 // dupStdio opens the slavePath for the console and dups the fds to the current 99 // processes stdio, fd 0,1,2. 100 func (c *linuxConsole) dupStdio() error { 101 slave, err := c.open(syscall.O_RDWR) 102 if err != nil { 103 return err 104 } 105 fd := int(slave.Fd()) 106 for _, i := range []int{0, 1, 2} { 107 if err := syscall.Dup3(fd, i, 0); err != nil { 108 return err 109 } 110 } 111 return nil 112 } 113 114 // open is a clone of os.OpenFile without the O_CLOEXEC used to open the pty slave. 115 func (c *linuxConsole) open(flag int) (*os.File, error) { 116 r, e := syscall.Open(c.slavePath, flag, 0) 117 if e != nil { 118 return nil, &os.PathError{ 119 Op: "open", 120 Path: c.slavePath, 121 Err: e, 122 } 123 } 124 return os.NewFile(uintptr(r), c.slavePath), nil 125 } 126 127 func ioctl(fd uintptr, flag, data uintptr) error { 128 if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, flag, data); err != 0 { 129 return err 130 } 131 return nil 132 } 133 134 // unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. 135 // unlockpt should be called before opening the slave side of a pty. 136 func unlockpt(f *os.File) error { 137 var u int32 138 return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) 139 } 140 141 // ptsname retrieves the name of the first available pts for the given master. 142 func ptsname(f *os.File) (string, error) { 143 var n int32 144 if err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))); err != nil { 145 return "", err 146 } 147 return fmt.Sprintf("/dev/pts/%d", n), nil 148 } 149 150 // saneTerminal sets the necessary tty_ioctl(4)s to ensure that a pty pair 151 // created by us acts normally. In particular, a not-very-well-known default of 152 // Linux unix98 ptys is that they have +onlcr by default. While this isn't a 153 // problem for terminal emulators, because we relay data from the terminal we 154 // also relay that funky line discipline. 155 func saneTerminal(terminal *os.File) error { 156 // Go doesn't have a wrapper for any of the termios ioctls. 157 var termios syscall.Termios 158 159 if err := ioctl(terminal.Fd(), syscall.TCGETS, uintptr(unsafe.Pointer(&termios))); err != nil { 160 return fmt.Errorf("ioctl(tty, tcgets): %s", err.Error()) 161 } 162 163 // Set -onlcr so we don't have to deal with \r. 164 termios.Oflag &^= syscall.ONLCR 165 166 if err := ioctl(terminal.Fd(), syscall.TCSETS, uintptr(unsafe.Pointer(&termios))); err != nil { 167 return fmt.Errorf("ioctl(tty, tcsets): %s", err.Error()) 168 } 169 170 return nil 171 }