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  }