// setupConsole sets up the console from inside the container, and sends the // master pty fd to the config.Pipe (using cmsg). This is done to ensure that // consoles are scoped to a container properly (see runc#814 and the many // issues related to that). This has to be run *after* we've pivoted to the new // rootfs (and the users' configuration is entirely set up). func setupConsole(pipe *os.File, config *initConfig, mount bool) error { // At this point, /dev/ptmx points to something that we would expect. We // used to change the owner of the slave path, but since the /dev/pts mount // can have gid=X set (at the users' option). So touching the owner of the // slave PTY is not necessary, as the kernel will handle that for us. Note // however, that setupUser (specifically fixStdioPermissions) *will* change // the UID owner of the console to be the user the process will run as (so // they can actually control their console). console, err := newConsole() if err != nil { return err } // After we return from here, we don't need the console anymore. defer console.Close() linuxConsole, ok := console.(*linuxConsole) if !ok { return fmt.Errorf("failed to cast console to *linuxConsole") } // Mount the console inside our rootfs. if mount { if err := linuxConsole.mount(); err != nil { return err } } if err := writeSync(pipe, procConsole); err != nil { return err } // We need to have a two-way synchronisation here. Though it might seem // pointless, it's important to make sure that the sendmsg(2) payload // doesn't get swallowed by an out-of-place read(2) [which happens if the // syscalls get reordered so that sendmsg(2) is before the other side's // read(2) of procConsole]. if err := readSync(pipe, procConsoleReq); err != nil { return err } // While we can access console.master, using the API is a good idea. if err := utils.SendFd(pipe, linuxConsole.File()); err != nil { return err } // Make sure the other side recieved the fd. if err := readSync(pipe, procConsoleAck); err != nil { return err } // Now, dup over all the things. return linuxConsole.dupStdio() }
func (t *tty) sendtty(socket *os.File, ti *libcontainer.TerminalInfo) error { if t.console == nil { return fmt.Errorf("tty.console not set") } // Create a fake file to contain the terminal info. console := os.NewFile(t.console.File().Fd(), ti.String()) if err := utils.SendFd(socket, console); err != nil { return err } return nil }