mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Refactor tty codebase to use master-replica terminology.
Updates #2972 PiperOrigin-RevId: 329584905
This commit is contained in:
@@ -54,9 +54,9 @@ const (
|
||||
// Unix98 PTY masters.
|
||||
UNIX98_PTY_MASTER_MAJOR = 128
|
||||
|
||||
// UNIX98_PTY_SLAVE_MAJOR is the initial major device number for
|
||||
// Unix98 PTY slaves.
|
||||
UNIX98_PTY_SLAVE_MAJOR = 136
|
||||
// UNIX98_PTY_REPLICA_MAJOR is the initial major device number for
|
||||
// Unix98 PTY replicas.
|
||||
UNIX98_PTY_REPLICA_MAJOR = 136
|
||||
)
|
||||
|
||||
// Minor device numbers for TTYAUX_MAJOR.
|
||||
|
||||
@@ -325,9 +325,9 @@ var MasterTermios = KernelTermios{
|
||||
OutputSpeed: 38400,
|
||||
}
|
||||
|
||||
// DefaultSlaveTermios is the default terminal configuration of the slave end
|
||||
// of a Unix98 pseudoterminal.
|
||||
var DefaultSlaveTermios = KernelTermios{
|
||||
// DefaultReplicaTermios is the default terminal configuration of the replica
|
||||
// end of a Unix98 pseudoterminal.
|
||||
var DefaultReplicaTermios = KernelTermios{
|
||||
InputFlags: ICRNL | IXON,
|
||||
OutputFlags: OPOST | ONLCR,
|
||||
ControlFlags: B38400 | CS8 | CREAD,
|
||||
|
||||
@@ -54,7 +54,7 @@ type TTYFileOperations struct {
|
||||
func newTTYFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags, iops *inodeOperations) *fs.File {
|
||||
return fs.NewFile(ctx, dirent, flags, &TTYFileOperations{
|
||||
fileOperations: fileOperations{iops: iops},
|
||||
termios: linux.DefaultSlaveTermios,
|
||||
termios: linux.DefaultReplicaTermios,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ go_library(
|
||||
"line_discipline.go",
|
||||
"master.go",
|
||||
"queue.go",
|
||||
"slave.go",
|
||||
"replica.go",
|
||||
"terminal.go",
|
||||
],
|
||||
visibility = ["//pkg/sentry:internal"],
|
||||
|
||||
+23
-23
@@ -37,14 +37,14 @@ import (
|
||||
// This indirectly manages all terminals within the mount.
|
||||
//
|
||||
// New Terminals are created by masterInodeOperations.GetFile, which registers
|
||||
// the slave Inode in the this directory for discovery via Lookup/Readdir. The
|
||||
// slave inode is unregistered when the master file is Released, as the slave
|
||||
// the replica Inode in the this directory for discovery via Lookup/Readdir. The
|
||||
// replica inode is unregistered when the master file is Released, as the replica
|
||||
// is no longer discoverable at that point.
|
||||
//
|
||||
// References on the underlying Terminal are held by masterFileOperations and
|
||||
// slaveInodeOperations.
|
||||
// replicaInodeOperations.
|
||||
//
|
||||
// masterInodeOperations and slaveInodeOperations hold a pointer to
|
||||
// masterInodeOperations and replicaInodeOperations hold a pointer to
|
||||
// dirInodeOperations, which is reference counted by the refcount their
|
||||
// corresponding Dirents hold on their parent (this directory).
|
||||
//
|
||||
@@ -76,16 +76,16 @@ type dirInodeOperations struct {
|
||||
// master is the master PTY inode.
|
||||
master *fs.Inode
|
||||
|
||||
// slaves contains the slave inodes reachable from the directory.
|
||||
// replicas contains the replica inodes reachable from the directory.
|
||||
//
|
||||
// A new slave is added by allocateTerminal and is removed by
|
||||
// A new replica is added by allocateTerminal and is removed by
|
||||
// masterFileOperations.Release.
|
||||
//
|
||||
// A reference is held on every slave in the map.
|
||||
slaves map[uint32]*fs.Inode
|
||||
// A reference is held on every replica in the map.
|
||||
replicas map[uint32]*fs.Inode
|
||||
|
||||
// dentryMap is a SortedDentryMap used to implement Readdir containing
|
||||
// the master and all entries in slaves.
|
||||
// the master and all entries in replicas.
|
||||
dentryMap *fs.SortedDentryMap
|
||||
|
||||
// next is the next pty index to use.
|
||||
@@ -101,7 +101,7 @@ func newDir(ctx context.Context, m *fs.MountSource) *fs.Inode {
|
||||
d := &dirInodeOperations{
|
||||
InodeSimpleAttributes: fsutil.NewInodeSimpleAttributes(ctx, fs.RootOwner, fs.FilePermsFromMode(0555), linux.DEVPTS_SUPER_MAGIC),
|
||||
msrc: m,
|
||||
slaves: make(map[uint32]*fs.Inode),
|
||||
replicas: make(map[uint32]*fs.Inode),
|
||||
dentryMap: fs.NewSortedDentryMap(nil),
|
||||
}
|
||||
// Linux devpts uses a default mode of 0000 for ptmx which can be
|
||||
@@ -133,7 +133,7 @@ func (d *dirInodeOperations) Release(ctx context.Context) {
|
||||
defer d.mu.Unlock()
|
||||
|
||||
d.master.DecRef(ctx)
|
||||
if len(d.slaves) != 0 {
|
||||
if len(d.replicas) != 0 {
|
||||
panic(fmt.Sprintf("devpts directory still contains active terminals: %+v", d))
|
||||
}
|
||||
}
|
||||
@@ -149,14 +149,14 @@ func (d *dirInodeOperations) Lookup(ctx context.Context, dir *fs.Inode, name str
|
||||
return fs.NewDirent(ctx, d.master, name), nil
|
||||
}
|
||||
|
||||
// Slave number?
|
||||
// Replica number?
|
||||
n, err := strconv.ParseUint(name, 10, 32)
|
||||
if err != nil {
|
||||
// Not found.
|
||||
return nil, syserror.ENOENT
|
||||
}
|
||||
|
||||
s, ok := d.slaves[uint32(n)]
|
||||
s, ok := d.replicas[uint32(n)]
|
||||
if !ok {
|
||||
return nil, syserror.ENOENT
|
||||
}
|
||||
@@ -236,7 +236,7 @@ func (d *dirInodeOperations) allocateTerminal(ctx context.Context) (*Terminal, e
|
||||
return nil, syserror.ENOMEM
|
||||
}
|
||||
|
||||
if _, ok := d.slaves[n]; ok {
|
||||
if _, ok := d.replicas[n]; ok {
|
||||
panic(fmt.Sprintf("pty index collision; index %d already exists", n))
|
||||
}
|
||||
|
||||
@@ -244,19 +244,19 @@ func (d *dirInodeOperations) allocateTerminal(ctx context.Context) (*Terminal, e
|
||||
d.next++
|
||||
|
||||
// The reference returned by newTerminal is returned to the caller.
|
||||
// Take another for the slave inode.
|
||||
// Take another for the replica inode.
|
||||
t.IncRef()
|
||||
|
||||
// Create a pts node. The owner is based on the context that opens
|
||||
// ptmx.
|
||||
creds := auth.CredentialsFromContext(ctx)
|
||||
uid, gid := creds.EffectiveKUID, creds.EffectiveKGID
|
||||
slave := newSlaveInode(ctx, d, t, fs.FileOwner{uid, gid}, fs.FilePermsFromMode(0666))
|
||||
replica := newReplicaInode(ctx, d, t, fs.FileOwner{uid, gid}, fs.FilePermsFromMode(0666))
|
||||
|
||||
d.slaves[n] = slave
|
||||
d.replicas[n] = replica
|
||||
d.dentryMap.Add(strconv.FormatUint(uint64(n), 10), fs.DentAttr{
|
||||
Type: slave.StableAttr.Type,
|
||||
InodeID: slave.StableAttr.InodeID,
|
||||
Type: replica.StableAttr.Type,
|
||||
InodeID: replica.StableAttr.InodeID,
|
||||
})
|
||||
|
||||
return t, nil
|
||||
@@ -267,18 +267,18 @@ func (d *dirInodeOperations) masterClose(ctx context.Context, t *Terminal) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
|
||||
// The slave end disappears from the directory when the master end is
|
||||
// closed, even if the slave end is open elsewhere.
|
||||
// The replica end disappears from the directory when the master end is
|
||||
// closed, even if the replica end is open elsewhere.
|
||||
//
|
||||
// N.B. since we're using a backdoor method to remove a directory entry
|
||||
// we won't properly fire inotify events like Linux would.
|
||||
s, ok := d.slaves[t.n]
|
||||
s, ok := d.replicas[t.n]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Terminal %+v doesn't exist in %+v?", t, d))
|
||||
}
|
||||
|
||||
s.DecRef(ctx)
|
||||
delete(d.slaves, t.n)
|
||||
delete(d.replicas, t.n)
|
||||
d.dentryMap.Remove(strconv.FormatUint(uint64(t.n), 10))
|
||||
}
|
||||
|
||||
|
||||
@@ -79,8 +79,8 @@ type superOperations struct{}
|
||||
//
|
||||
// It always returns true, forcing a Lookup for all entries.
|
||||
//
|
||||
// Slave entries are dropped from dir when their master is closed, so an
|
||||
// existing slave Dirent in the tree is not sufficient to guarantee that it
|
||||
// Replica entries are dropped from dir when their master is closed, so an
|
||||
// existing replica Dirent in the tree is not sufficient to guarantee that it
|
||||
// still exists on the filesystem.
|
||||
func (superOperations) Revalidate(context.Context, string, *fs.Inode, *fs.Inode) bool {
|
||||
return true
|
||||
|
||||
@@ -44,7 +44,7 @@ const (
|
||||
)
|
||||
|
||||
// lineDiscipline dictates how input and output are handled between the
|
||||
// pseudoterminal (pty) master and slave. It can be configured to alter I/O,
|
||||
// pseudoterminal (pty) master and replica. It can be configured to alter I/O,
|
||||
// modify control characters (e.g. Ctrl-C for SIGINT), etc. The following man
|
||||
// pages are good resources for how to affect the line discipline:
|
||||
//
|
||||
@@ -55,8 +55,8 @@ const (
|
||||
//
|
||||
// lineDiscipline has a simple structure but supports a multitude of options
|
||||
// (see the above man pages). It consists of two queues of bytes: one from the
|
||||
// terminal master to slave (the input queue) and one from slave to master (the
|
||||
// output queue). When bytes are written to one end of the pty, the line
|
||||
// terminal master to replica (the input queue) and one from replica to master
|
||||
// (the output queue). When bytes are written to one end of the pty, the line
|
||||
// discipline reads the bytes, modifies them or takes special action if
|
||||
// required, and enqueues them to be read by the other end of the pty:
|
||||
//
|
||||
@@ -65,7 +65,7 @@ const (
|
||||
// | (inputQueueWrite) +-------------+ (inputQueueRead) |
|
||||
// | |
|
||||
// | v
|
||||
// masterFD slaveFD
|
||||
// masterFD replicaFD
|
||||
// ^ |
|
||||
// | |
|
||||
// | output to terminal +--------------+ output from process |
|
||||
@@ -104,8 +104,8 @@ type lineDiscipline struct {
|
||||
// masterWaiter is used to wait on the master end of the TTY.
|
||||
masterWaiter waiter.Queue `state:"zerovalue"`
|
||||
|
||||
// slaveWaiter is used to wait on the slave end of the TTY.
|
||||
slaveWaiter waiter.Queue `state:"zerovalue"`
|
||||
// replicaWaiter is used to wait on the replica end of the TTY.
|
||||
replicaWaiter waiter.Queue `state:"zerovalue"`
|
||||
}
|
||||
|
||||
func newLineDiscipline(termios linux.KernelTermios) *lineDiscipline {
|
||||
@@ -143,7 +143,7 @@ func (l *lineDiscipline) setTermios(task *kernel.Task, args arch.SyscallArgument
|
||||
l.inQueue.pushWaitBufLocked(l)
|
||||
l.inQueue.readable = true
|
||||
l.inQueue.mu.Unlock()
|
||||
l.slaveWaiter.Notify(waiter.EventIn)
|
||||
l.replicaWaiter.Notify(waiter.EventIn)
|
||||
}
|
||||
|
||||
return 0, err
|
||||
@@ -169,7 +169,7 @@ func (l *lineDiscipline) masterReadiness() waiter.EventMask {
|
||||
return l.inQueue.writeReadiness(&linux.MasterTermios) | l.outQueue.readReadiness(&linux.MasterTermios)
|
||||
}
|
||||
|
||||
func (l *lineDiscipline) slaveReadiness() waiter.EventMask {
|
||||
func (l *lineDiscipline) replicaReadiness() waiter.EventMask {
|
||||
l.termiosMu.RLock()
|
||||
defer l.termiosMu.RUnlock()
|
||||
return l.outQueue.writeReadiness(&l.termios) | l.inQueue.readReadiness(&l.termios)
|
||||
@@ -189,7 +189,7 @@ func (l *lineDiscipline) inputQueueRead(ctx context.Context, dst usermem.IOSeque
|
||||
if n > 0 {
|
||||
l.masterWaiter.Notify(waiter.EventOut)
|
||||
if pushed {
|
||||
l.slaveWaiter.Notify(waiter.EventIn)
|
||||
l.replicaWaiter.Notify(waiter.EventIn)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
@@ -204,7 +204,7 @@ func (l *lineDiscipline) inputQueueWrite(ctx context.Context, src usermem.IOSequ
|
||||
return 0, err
|
||||
}
|
||||
if n > 0 {
|
||||
l.slaveWaiter.Notify(waiter.EventIn)
|
||||
l.replicaWaiter.Notify(waiter.EventIn)
|
||||
return n, nil
|
||||
}
|
||||
return 0, syserror.ErrWouldBlock
|
||||
@@ -222,7 +222,7 @@ func (l *lineDiscipline) outputQueueRead(ctx context.Context, dst usermem.IOSequ
|
||||
return 0, err
|
||||
}
|
||||
if n > 0 {
|
||||
l.slaveWaiter.Notify(waiter.EventOut)
|
||||
l.replicaWaiter.Notify(waiter.EventOut)
|
||||
if pushed {
|
||||
l.masterWaiter.Notify(waiter.EventIn)
|
||||
}
|
||||
|
||||
@@ -166,11 +166,11 @@ func (mf *masterFileOperations) Ioctl(ctx context.Context, _ *fs.File, io userme
|
||||
return 0, mf.t.ld.outputQueueReadSize(t, args)
|
||||
case linux.TCGETS:
|
||||
// N.B. TCGETS on the master actually returns the configuration
|
||||
// of the slave end.
|
||||
// of the replica end.
|
||||
return mf.t.ld.getTermios(t, args)
|
||||
case linux.TCSETS:
|
||||
// N.B. TCSETS on the master actually affects the configuration
|
||||
// of the slave end.
|
||||
// of the replica end.
|
||||
return mf.t.ld.setTermios(t, args)
|
||||
case linux.TCSETSW:
|
||||
// TODO(b/29356795): This should drain the output queue first.
|
||||
|
||||
@@ -34,7 +34,7 @@ import (
|
||||
const waitBufMaxBytes = 131072
|
||||
|
||||
// queue represents one of the input or output queues between a pty master and
|
||||
// slave. Bytes written to a queue are added to the read buffer until it is
|
||||
// replica. Bytes written to a queue are added to the read buffer until it is
|
||||
// full, at which point they are written to the wait buffer. Bytes are
|
||||
// processed (i.e. undergo termios transformations) as they are added to the
|
||||
// read buffer. The read buffer is readable when its length is nonzero and
|
||||
|
||||
@@ -29,11 +29,11 @@ import (
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
// slaveInodeOperations are the fs.InodeOperations for the slave end of the
|
||||
// replicaInodeOperations are the fs.InodeOperations for the replica end of the
|
||||
// Terminal (pts file).
|
||||
//
|
||||
// +stateify savable
|
||||
type slaveInodeOperations struct {
|
||||
type replicaInodeOperations struct {
|
||||
fsutil.SimpleFileInode
|
||||
|
||||
// d is the containing dir.
|
||||
@@ -43,13 +43,13 @@ type slaveInodeOperations struct {
|
||||
t *Terminal
|
||||
}
|
||||
|
||||
var _ fs.InodeOperations = (*slaveInodeOperations)(nil)
|
||||
var _ fs.InodeOperations = (*replicaInodeOperations)(nil)
|
||||
|
||||
// newSlaveInode creates an fs.Inode for the slave end of a terminal.
|
||||
// newReplicaInode creates an fs.Inode for the replica end of a terminal.
|
||||
//
|
||||
// newSlaveInode takes ownership of t.
|
||||
func newSlaveInode(ctx context.Context, d *dirInodeOperations, t *Terminal, owner fs.FileOwner, p fs.FilePermissions) *fs.Inode {
|
||||
iops := &slaveInodeOperations{
|
||||
// newReplicaInode takes ownership of t.
|
||||
func newReplicaInode(ctx context.Context, d *dirInodeOperations, t *Terminal, owner fs.FileOwner, p fs.FilePermissions) *fs.Inode {
|
||||
iops := &replicaInodeOperations{
|
||||
SimpleFileInode: *fsutil.NewSimpleFileInode(ctx, owner, p, linux.DEVPTS_SUPER_MAGIC),
|
||||
d: d,
|
||||
t: t,
|
||||
@@ -66,18 +66,18 @@ func newSlaveInode(ctx context.Context, d *dirInodeOperations, t *Terminal, owne
|
||||
Type: fs.CharacterDevice,
|
||||
// See fs/devpts/inode.c:devpts_fill_super.
|
||||
BlockSize: 1024,
|
||||
DeviceFileMajor: linux.UNIX98_PTY_SLAVE_MAJOR,
|
||||
DeviceFileMajor: linux.UNIX98_PTY_REPLICA_MAJOR,
|
||||
DeviceFileMinor: t.n,
|
||||
})
|
||||
}
|
||||
|
||||
// Release implements fs.InodeOperations.Release.
|
||||
func (si *slaveInodeOperations) Release(ctx context.Context) {
|
||||
func (si *replicaInodeOperations) Release(ctx context.Context) {
|
||||
si.t.DecRef(ctx)
|
||||
}
|
||||
|
||||
// Truncate implements fs.InodeOperations.Truncate.
|
||||
func (*slaveInodeOperations) Truncate(context.Context, *fs.Inode, int64) error {
|
||||
func (*replicaInodeOperations) Truncate(context.Context, *fs.Inode, int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -85,14 +85,15 @@ func (*slaveInodeOperations) Truncate(context.Context, *fs.Inode, int64) error {
|
||||
//
|
||||
// This may race with destruction of the terminal. If the terminal is gone, it
|
||||
// returns ENOENT.
|
||||
func (si *slaveInodeOperations) GetFile(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
|
||||
return fs.NewFile(ctx, d, flags, &slaveFileOperations{si: si}), nil
|
||||
func (si *replicaInodeOperations) GetFile(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
|
||||
return fs.NewFile(ctx, d, flags, &replicaFileOperations{si: si}), nil
|
||||
}
|
||||
|
||||
// slaveFileOperations are the fs.FileOperations for the slave end of a terminal.
|
||||
// replicaFileOperations are the fs.FileOperations for the replica end of a
|
||||
// terminal.
|
||||
//
|
||||
// +stateify savable
|
||||
type slaveFileOperations struct {
|
||||
type replicaFileOperations struct {
|
||||
fsutil.FilePipeSeek `state:"nosave"`
|
||||
fsutil.FileNotDirReaddir `state:"nosave"`
|
||||
fsutil.FileNoFsync `state:"nosave"`
|
||||
@@ -102,42 +103,42 @@ type slaveFileOperations struct {
|
||||
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
|
||||
|
||||
// si is the inode operations.
|
||||
si *slaveInodeOperations
|
||||
si *replicaInodeOperations
|
||||
}
|
||||
|
||||
var _ fs.FileOperations = (*slaveFileOperations)(nil)
|
||||
var _ fs.FileOperations = (*replicaFileOperations)(nil)
|
||||
|
||||
// Release implements fs.FileOperations.Release.
|
||||
func (sf *slaveFileOperations) Release(context.Context) {
|
||||
func (sf *replicaFileOperations) Release(context.Context) {
|
||||
}
|
||||
|
||||
// EventRegister implements waiter.Waitable.EventRegister.
|
||||
func (sf *slaveFileOperations) EventRegister(e *waiter.Entry, mask waiter.EventMask) {
|
||||
sf.si.t.ld.slaveWaiter.EventRegister(e, mask)
|
||||
func (sf *replicaFileOperations) EventRegister(e *waiter.Entry, mask waiter.EventMask) {
|
||||
sf.si.t.ld.replicaWaiter.EventRegister(e, mask)
|
||||
}
|
||||
|
||||
// EventUnregister implements waiter.Waitable.EventUnregister.
|
||||
func (sf *slaveFileOperations) EventUnregister(e *waiter.Entry) {
|
||||
sf.si.t.ld.slaveWaiter.EventUnregister(e)
|
||||
func (sf *replicaFileOperations) EventUnregister(e *waiter.Entry) {
|
||||
sf.si.t.ld.replicaWaiter.EventUnregister(e)
|
||||
}
|
||||
|
||||
// Readiness implements waiter.Waitable.Readiness.
|
||||
func (sf *slaveFileOperations) Readiness(mask waiter.EventMask) waiter.EventMask {
|
||||
return sf.si.t.ld.slaveReadiness()
|
||||
func (sf *replicaFileOperations) Readiness(mask waiter.EventMask) waiter.EventMask {
|
||||
return sf.si.t.ld.replicaReadiness()
|
||||
}
|
||||
|
||||
// Read implements fs.FileOperations.Read.
|
||||
func (sf *slaveFileOperations) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, _ int64) (int64, error) {
|
||||
func (sf *replicaFileOperations) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, _ int64) (int64, error) {
|
||||
return sf.si.t.ld.inputQueueRead(ctx, dst)
|
||||
}
|
||||
|
||||
// Write implements fs.FileOperations.Write.
|
||||
func (sf *slaveFileOperations) Write(ctx context.Context, _ *fs.File, src usermem.IOSequence, _ int64) (int64, error) {
|
||||
func (sf *replicaFileOperations) Write(ctx context.Context, _ *fs.File, src usermem.IOSequence, _ int64) (int64, error) {
|
||||
return sf.si.t.ld.outputQueueWrite(ctx, src)
|
||||
}
|
||||
|
||||
// Ioctl implements fs.FileOperations.Ioctl.
|
||||
func (sf *slaveFileOperations) Ioctl(ctx context.Context, _ *fs.File, io usermem.IO, args arch.SyscallArguments) (uintptr, error) {
|
||||
func (sf *replicaFileOperations) Ioctl(ctx context.Context, _ *fs.File, io usermem.IO, args arch.SyscallArguments) (uintptr, error) {
|
||||
t := kernel.TaskFromContext(ctx)
|
||||
if t == nil {
|
||||
// ioctl(2) may only be called from a task goroutine.
|
||||
@@ -182,4 +183,4 @@ func (sf *slaveFileOperations) Ioctl(ctx context.Context, _ *fs.File, io usermem
|
||||
}
|
||||
}
|
||||
|
||||
// LINT.ThenChange(../../fsimpl/devpts/slave.go)
|
||||
// LINT.ThenChange(../../fsimpl/devpts/replica.go)
|
||||
@@ -44,19 +44,19 @@ type Terminal struct {
|
||||
// this terminal. This field is immutable.
|
||||
masterKTTY *kernel.TTY
|
||||
|
||||
// slaveKTTY contains the controlling process of the slave end of this
|
||||
// replicaKTTY contains the controlling process of the replica end of this
|
||||
// terminal. This field is immutable.
|
||||
slaveKTTY *kernel.TTY
|
||||
replicaKTTY *kernel.TTY
|
||||
}
|
||||
|
||||
func newTerminal(ctx context.Context, d *dirInodeOperations, n uint32) *Terminal {
|
||||
termios := linux.DefaultSlaveTermios
|
||||
termios := linux.DefaultReplicaTermios
|
||||
t := Terminal{
|
||||
d: d,
|
||||
n: n,
|
||||
ld: newLineDiscipline(termios),
|
||||
masterKTTY: &kernel.TTY{Index: n},
|
||||
slaveKTTY: &kernel.TTY{Index: n},
|
||||
d: d,
|
||||
n: n,
|
||||
ld: newLineDiscipline(termios),
|
||||
masterKTTY: &kernel.TTY{Index: n},
|
||||
replicaKTTY: &kernel.TTY{Index: n},
|
||||
}
|
||||
t.EnableLeakCheck("tty.Terminal")
|
||||
return &t
|
||||
@@ -123,7 +123,7 @@ func (tm *Terminal) tty(isMaster bool) *kernel.TTY {
|
||||
if isMaster {
|
||||
return tm.masterKTTY
|
||||
}
|
||||
return tm.slaveKTTY
|
||||
return tm.replicaKTTY
|
||||
}
|
||||
|
||||
// LINT.ThenChange(../../fsimpl/devpts/terminal.go)
|
||||
|
||||
@@ -22,8 +22,8 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
)
|
||||
|
||||
func TestSimpleMasterToSlave(t *testing.T) {
|
||||
ld := newLineDiscipline(linux.DefaultSlaveTermios)
|
||||
func TestSimpleMasterToReplica(t *testing.T) {
|
||||
ld := newLineDiscipline(linux.DefaultReplicaTermios)
|
||||
ctx := contexttest.Context(t)
|
||||
inBytes := []byte("hello, tty\n")
|
||||
src := usermem.BytesIOSequence(inBytes)
|
||||
|
||||
@@ -21,8 +21,8 @@ go_library(
|
||||
"line_discipline.go",
|
||||
"master.go",
|
||||
"queue.go",
|
||||
"replica.go",
|
||||
"root_inode_refs.go",
|
||||
"slave.go",
|
||||
"terminal.go",
|
||||
],
|
||||
visibility = ["//pkg/sentry:internal"],
|
||||
|
||||
@@ -79,7 +79,7 @@ func (fstype FilesystemType) newFilesystem(vfsObj *vfs.VirtualFilesystem, creds
|
||||
|
||||
// Construct the root directory. This is always inode id 1.
|
||||
root := &rootInode{
|
||||
slaves: make(map[uint32]*slaveInode),
|
||||
replicas: make(map[uint32]*replicaInode),
|
||||
}
|
||||
root.InodeAttrs.Init(creds, linux.UNNAMED_MAJOR, devMinor, 1, linux.ModeDirectory|0555)
|
||||
root.OrderedChildren.Init(kernfs.OrderedChildrenOptions{})
|
||||
@@ -133,8 +133,8 @@ type rootInode struct {
|
||||
// mu protects the fields below.
|
||||
mu sync.Mutex
|
||||
|
||||
// slaves maps pty ids to slave inodes.
|
||||
slaves map[uint32]*slaveInode
|
||||
// replicas maps pty ids to replica inodes.
|
||||
replicas map[uint32]*replicaInode
|
||||
|
||||
// nextIdx is the next pty index to use. Must be accessed atomically.
|
||||
//
|
||||
@@ -154,22 +154,22 @@ func (i *rootInode) allocateTerminal(creds *auth.Credentials) (*Terminal, error)
|
||||
idx := i.nextIdx
|
||||
i.nextIdx++
|
||||
|
||||
// Sanity check that slave with idx does not exist.
|
||||
if _, ok := i.slaves[idx]; ok {
|
||||
// Sanity check that replica with idx does not exist.
|
||||
if _, ok := i.replicas[idx]; ok {
|
||||
panic(fmt.Sprintf("pty index collision; index %d already exists", idx))
|
||||
}
|
||||
|
||||
// Create the new terminal and slave.
|
||||
// Create the new terminal and replica.
|
||||
t := newTerminal(idx)
|
||||
slave := &slaveInode{
|
||||
replica := &replicaInode{
|
||||
root: i,
|
||||
t: t,
|
||||
}
|
||||
// Linux always uses pty index + 3 as the inode id. See
|
||||
// fs/devpts/inode.c:devpts_pty_new().
|
||||
slave.InodeAttrs.Init(creds, i.InodeAttrs.DevMajor(), i.InodeAttrs.DevMinor(), uint64(idx+3), linux.ModeCharacterDevice|0600)
|
||||
slave.dentry.Init(slave)
|
||||
i.slaves[idx] = slave
|
||||
replica.InodeAttrs.Init(creds, i.InodeAttrs.DevMajor(), i.InodeAttrs.DevMinor(), uint64(idx+3), linux.ModeCharacterDevice|0600)
|
||||
replica.dentry.Init(replica)
|
||||
i.replicas[idx] = replica
|
||||
|
||||
return t, nil
|
||||
}
|
||||
@@ -179,11 +179,11 @@ func (i *rootInode) masterClose(t *Terminal) {
|
||||
i.mu.Lock()
|
||||
defer i.mu.Unlock()
|
||||
|
||||
// Sanity check that slave with idx exists.
|
||||
if _, ok := i.slaves[t.n]; !ok {
|
||||
// Sanity check that replica with idx exists.
|
||||
if _, ok := i.replicas[t.n]; !ok {
|
||||
panic(fmt.Sprintf("pty with index %d does not exist", t.n))
|
||||
}
|
||||
delete(i.slaves, t.n)
|
||||
delete(i.replicas, t.n)
|
||||
}
|
||||
|
||||
// Open implements kernfs.Inode.Open.
|
||||
@@ -205,7 +205,7 @@ func (i *rootInode) Lookup(ctx context.Context, name string) (*vfs.Dentry, error
|
||||
}
|
||||
i.mu.Lock()
|
||||
defer i.mu.Unlock()
|
||||
if si, ok := i.slaves[uint32(idx)]; ok {
|
||||
if si, ok := i.replicas[uint32(idx)]; ok {
|
||||
si.dentry.IncRef()
|
||||
return si.dentry.VFSDentry(), nil
|
||||
|
||||
@@ -217,8 +217,8 @@ func (i *rootInode) Lookup(ctx context.Context, name string) (*vfs.Dentry, error
|
||||
func (i *rootInode) IterDirents(ctx context.Context, cb vfs.IterDirentsCallback, offset, relOffset int64) (int64, error) {
|
||||
i.mu.Lock()
|
||||
defer i.mu.Unlock()
|
||||
ids := make([]int, 0, len(i.slaves))
|
||||
for id := range i.slaves {
|
||||
ids := make([]int, 0, len(i.replicas))
|
||||
for id := range i.replicas {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
@@ -226,7 +226,7 @@ func (i *rootInode) IterDirents(ctx context.Context, cb vfs.IterDirentsCallback,
|
||||
dirent := vfs.Dirent{
|
||||
Name: strconv.FormatUint(uint64(id), 10),
|
||||
Type: linux.DT_CHR,
|
||||
Ino: i.slaves[uint32(id)].InodeAttrs.Ino(),
|
||||
Ino: i.replicas[uint32(id)].InodeAttrs.Ino(),
|
||||
NextOff: offset + 1,
|
||||
}
|
||||
if err := cb.Handle(dirent); err != nil {
|
||||
|
||||
@@ -22,8 +22,8 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
)
|
||||
|
||||
func TestSimpleMasterToSlave(t *testing.T) {
|
||||
ld := newLineDiscipline(linux.DefaultSlaveTermios)
|
||||
func TestSimpleMasterToReplica(t *testing.T) {
|
||||
ld := newLineDiscipline(linux.DefaultReplicaTermios)
|
||||
ctx := contexttest.Context(t)
|
||||
inBytes := []byte("hello, tty\n")
|
||||
src := usermem.BytesIOSequence(inBytes)
|
||||
|
||||
@@ -42,7 +42,7 @@ const (
|
||||
)
|
||||
|
||||
// lineDiscipline dictates how input and output are handled between the
|
||||
// pseudoterminal (pty) master and slave. It can be configured to alter I/O,
|
||||
// pseudoterminal (pty) master and replica. It can be configured to alter I/O,
|
||||
// modify control characters (e.g. Ctrl-C for SIGINT), etc. The following man
|
||||
// pages are good resources for how to affect the line discipline:
|
||||
//
|
||||
@@ -53,8 +53,8 @@ const (
|
||||
//
|
||||
// lineDiscipline has a simple structure but supports a multitude of options
|
||||
// (see the above man pages). It consists of two queues of bytes: one from the
|
||||
// terminal master to slave (the input queue) and one from slave to master (the
|
||||
// output queue). When bytes are written to one end of the pty, the line
|
||||
// terminal master to replica (the input queue) and one from replica to master
|
||||
// (the output queue). When bytes are written to one end of the pty, the line
|
||||
// discipline reads the bytes, modifies them or takes special action if
|
||||
// required, and enqueues them to be read by the other end of the pty:
|
||||
//
|
||||
@@ -63,7 +63,7 @@ const (
|
||||
// | (inputQueueWrite) +-------------+ (inputQueueRead) |
|
||||
// | |
|
||||
// | v
|
||||
// masterFD slaveFD
|
||||
// masterFD replicaFD
|
||||
// ^ |
|
||||
// | |
|
||||
// | output to terminal +--------------+ output from process |
|
||||
@@ -102,8 +102,8 @@ type lineDiscipline struct {
|
||||
// masterWaiter is used to wait on the master end of the TTY.
|
||||
masterWaiter waiter.Queue `state:"zerovalue"`
|
||||
|
||||
// slaveWaiter is used to wait on the slave end of the TTY.
|
||||
slaveWaiter waiter.Queue `state:"zerovalue"`
|
||||
// replicaWaiter is used to wait on the replica end of the TTY.
|
||||
replicaWaiter waiter.Queue `state:"zerovalue"`
|
||||
}
|
||||
|
||||
func newLineDiscipline(termios linux.KernelTermios) *lineDiscipline {
|
||||
@@ -141,7 +141,7 @@ func (l *lineDiscipline) setTermios(task *kernel.Task, args arch.SyscallArgument
|
||||
l.inQueue.pushWaitBufLocked(l)
|
||||
l.inQueue.readable = true
|
||||
l.inQueue.mu.Unlock()
|
||||
l.slaveWaiter.Notify(waiter.EventIn)
|
||||
l.replicaWaiter.Notify(waiter.EventIn)
|
||||
}
|
||||
|
||||
return 0, err
|
||||
@@ -167,7 +167,7 @@ func (l *lineDiscipline) masterReadiness() waiter.EventMask {
|
||||
return l.inQueue.writeReadiness(&linux.MasterTermios) | l.outQueue.readReadiness(&linux.MasterTermios)
|
||||
}
|
||||
|
||||
func (l *lineDiscipline) slaveReadiness() waiter.EventMask {
|
||||
func (l *lineDiscipline) replicaReadiness() waiter.EventMask {
|
||||
l.termiosMu.RLock()
|
||||
defer l.termiosMu.RUnlock()
|
||||
return l.outQueue.writeReadiness(&l.termios) | l.inQueue.readReadiness(&l.termios)
|
||||
@@ -187,7 +187,7 @@ func (l *lineDiscipline) inputQueueRead(ctx context.Context, dst usermem.IOSeque
|
||||
if n > 0 {
|
||||
l.masterWaiter.Notify(waiter.EventOut)
|
||||
if pushed {
|
||||
l.slaveWaiter.Notify(waiter.EventIn)
|
||||
l.replicaWaiter.Notify(waiter.EventIn)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
@@ -202,7 +202,7 @@ func (l *lineDiscipline) inputQueueWrite(ctx context.Context, src usermem.IOSequ
|
||||
return 0, err
|
||||
}
|
||||
if n > 0 {
|
||||
l.slaveWaiter.Notify(waiter.EventIn)
|
||||
l.replicaWaiter.Notify(waiter.EventIn)
|
||||
return n, nil
|
||||
}
|
||||
return 0, syserror.ErrWouldBlock
|
||||
@@ -220,7 +220,7 @@ func (l *lineDiscipline) outputQueueRead(ctx context.Context, dst usermem.IOSequ
|
||||
return 0, err
|
||||
}
|
||||
if n > 0 {
|
||||
l.slaveWaiter.Notify(waiter.EventOut)
|
||||
l.replicaWaiter.Notify(waiter.EventOut)
|
||||
if pushed {
|
||||
l.masterWaiter.Notify(waiter.EventIn)
|
||||
}
|
||||
|
||||
@@ -145,11 +145,11 @@ func (mfd *masterFileDescription) Ioctl(ctx context.Context, io usermem.IO, args
|
||||
return 0, mfd.t.ld.outputQueueReadSize(t, io, args)
|
||||
case linux.TCGETS:
|
||||
// N.B. TCGETS on the master actually returns the configuration
|
||||
// of the slave end.
|
||||
// of the replica end.
|
||||
return mfd.t.ld.getTermios(t, args)
|
||||
case linux.TCSETS:
|
||||
// N.B. TCSETS on the master actually affects the configuration
|
||||
// of the slave end.
|
||||
// of the replica end.
|
||||
return mfd.t.ld.setTermios(t, args)
|
||||
case linux.TCSETSW:
|
||||
// TODO(b/29356795): This should drain the output queue first.
|
||||
|
||||
@@ -32,7 +32,7 @@ import (
|
||||
const waitBufMaxBytes = 131072
|
||||
|
||||
// queue represents one of the input or output queues between a pty master and
|
||||
// slave. Bytes written to a queue are added to the read buffer until it is
|
||||
// replica. Bytes written to a queue are added to the read buffer until it is
|
||||
// full, at which point they are written to the wait buffer. Bytes are
|
||||
// processed (i.e. undergo termios transformations) as they are added to the
|
||||
// read buffer. The read buffer is readable when its length is nonzero and
|
||||
|
||||
@@ -29,8 +29,8 @@ import (
|
||||
"gvisor.dev/gvisor/tools/go_marshal/primitive"
|
||||
)
|
||||
|
||||
// slaveInode is the inode for the slave end of the Terminal.
|
||||
type slaveInode struct {
|
||||
// replicaInode is the inode for the replica end of the Terminal.
|
||||
type replicaInode struct {
|
||||
implStatFS
|
||||
kernfs.InodeAttrs
|
||||
kernfs.InodeNoopRefCount
|
||||
@@ -49,12 +49,12 @@ type slaveInode struct {
|
||||
t *Terminal
|
||||
}
|
||||
|
||||
var _ kernfs.Inode = (*slaveInode)(nil)
|
||||
var _ kernfs.Inode = (*replicaInode)(nil)
|
||||
|
||||
// Open implements kernfs.Inode.Open.
|
||||
func (si *slaveInode) Open(ctx context.Context, rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
|
||||
func (si *replicaInode) Open(ctx context.Context, rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
|
||||
si.IncRef()
|
||||
fd := &slaveFileDescription{
|
||||
fd := &replicaFileDescription{
|
||||
inode: si,
|
||||
}
|
||||
fd.LockFD.Init(&si.locks)
|
||||
@@ -67,76 +67,76 @@ func (si *slaveInode) Open(ctx context.Context, rp *vfs.ResolvingPath, vfsd *vfs
|
||||
}
|
||||
|
||||
// Valid implements kernfs.Inode.Valid.
|
||||
func (si *slaveInode) Valid(context.Context) bool {
|
||||
// Return valid if the slave still exists.
|
||||
func (si *replicaInode) Valid(context.Context) bool {
|
||||
// Return valid if the replica still exists.
|
||||
si.root.mu.Lock()
|
||||
defer si.root.mu.Unlock()
|
||||
_, ok := si.root.slaves[si.t.n]
|
||||
_, ok := si.root.replicas[si.t.n]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Stat implements kernfs.Inode.Stat.
|
||||
func (si *slaveInode) Stat(ctx context.Context, vfsfs *vfs.Filesystem, opts vfs.StatOptions) (linux.Statx, error) {
|
||||
func (si *replicaInode) Stat(ctx context.Context, vfsfs *vfs.Filesystem, opts vfs.StatOptions) (linux.Statx, error) {
|
||||
statx, err := si.InodeAttrs.Stat(ctx, vfsfs, opts)
|
||||
if err != nil {
|
||||
return linux.Statx{}, err
|
||||
}
|
||||
statx.Blksize = 1024
|
||||
statx.RdevMajor = linux.UNIX98_PTY_SLAVE_MAJOR
|
||||
statx.RdevMajor = linux.UNIX98_PTY_REPLICA_MAJOR
|
||||
statx.RdevMinor = si.t.n
|
||||
return statx, nil
|
||||
}
|
||||
|
||||
// SetStat implements kernfs.Inode.SetStat
|
||||
func (si *slaveInode) SetStat(ctx context.Context, vfsfs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions) error {
|
||||
func (si *replicaInode) SetStat(ctx context.Context, vfsfs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions) error {
|
||||
if opts.Stat.Mask&linux.STATX_SIZE != 0 {
|
||||
return syserror.EINVAL
|
||||
}
|
||||
return si.InodeAttrs.SetStat(ctx, vfsfs, creds, opts)
|
||||
}
|
||||
|
||||
type slaveFileDescription struct {
|
||||
type replicaFileDescription struct {
|
||||
vfsfd vfs.FileDescription
|
||||
vfs.FileDescriptionDefaultImpl
|
||||
vfs.LockFD
|
||||
|
||||
inode *slaveInode
|
||||
inode *replicaInode
|
||||
}
|
||||
|
||||
var _ vfs.FileDescriptionImpl = (*slaveFileDescription)(nil)
|
||||
var _ vfs.FileDescriptionImpl = (*replicaFileDescription)(nil)
|
||||
|
||||
// Release implements fs.FileOperations.Release.
|
||||
func (sfd *slaveFileDescription) Release(ctx context.Context) {
|
||||
func (sfd *replicaFileDescription) Release(ctx context.Context) {
|
||||
sfd.inode.DecRef(ctx)
|
||||
}
|
||||
|
||||
// EventRegister implements waiter.Waitable.EventRegister.
|
||||
func (sfd *slaveFileDescription) EventRegister(e *waiter.Entry, mask waiter.EventMask) {
|
||||
sfd.inode.t.ld.slaveWaiter.EventRegister(e, mask)
|
||||
func (sfd *replicaFileDescription) EventRegister(e *waiter.Entry, mask waiter.EventMask) {
|
||||
sfd.inode.t.ld.replicaWaiter.EventRegister(e, mask)
|
||||
}
|
||||
|
||||
// EventUnregister implements waiter.Waitable.EventUnregister.
|
||||
func (sfd *slaveFileDescription) EventUnregister(e *waiter.Entry) {
|
||||
sfd.inode.t.ld.slaveWaiter.EventUnregister(e)
|
||||
func (sfd *replicaFileDescription) EventUnregister(e *waiter.Entry) {
|
||||
sfd.inode.t.ld.replicaWaiter.EventUnregister(e)
|
||||
}
|
||||
|
||||
// Readiness implements waiter.Waitable.Readiness.
|
||||
func (sfd *slaveFileDescription) Readiness(mask waiter.EventMask) waiter.EventMask {
|
||||
return sfd.inode.t.ld.slaveReadiness()
|
||||
func (sfd *replicaFileDescription) Readiness(mask waiter.EventMask) waiter.EventMask {
|
||||
return sfd.inode.t.ld.replicaReadiness()
|
||||
}
|
||||
|
||||
// Read implements vfs.FileDescriptionImpl.Read.
|
||||
func (sfd *slaveFileDescription) Read(ctx context.Context, dst usermem.IOSequence, _ vfs.ReadOptions) (int64, error) {
|
||||
func (sfd *replicaFileDescription) Read(ctx context.Context, dst usermem.IOSequence, _ vfs.ReadOptions) (int64, error) {
|
||||
return sfd.inode.t.ld.inputQueueRead(ctx, dst)
|
||||
}
|
||||
|
||||
// Write implements vfs.FileDescriptionImpl.Write.
|
||||
func (sfd *slaveFileDescription) Write(ctx context.Context, src usermem.IOSequence, _ vfs.WriteOptions) (int64, error) {
|
||||
func (sfd *replicaFileDescription) Write(ctx context.Context, src usermem.IOSequence, _ vfs.WriteOptions) (int64, error) {
|
||||
return sfd.inode.t.ld.outputQueueWrite(ctx, src)
|
||||
}
|
||||
|
||||
// Ioctl implements vfs.FileDescriptionImpl.Ioctl.
|
||||
func (sfd *slaveFileDescription) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) {
|
||||
func (sfd *replicaFileDescription) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) {
|
||||
t := kernel.TaskFromContext(ctx)
|
||||
if t == nil {
|
||||
// ioctl(2) may only be called from a task goroutine.
|
||||
@@ -182,24 +182,24 @@ func (sfd *slaveFileDescription) Ioctl(ctx context.Context, io usermem.IO, args
|
||||
}
|
||||
|
||||
// SetStat implements vfs.FileDescriptionImpl.SetStat.
|
||||
func (sfd *slaveFileDescription) SetStat(ctx context.Context, opts vfs.SetStatOptions) error {
|
||||
func (sfd *replicaFileDescription) SetStat(ctx context.Context, opts vfs.SetStatOptions) error {
|
||||
creds := auth.CredentialsFromContext(ctx)
|
||||
fs := sfd.vfsfd.VirtualDentry().Mount().Filesystem()
|
||||
return sfd.inode.SetStat(ctx, fs, creds, opts)
|
||||
}
|
||||
|
||||
// Stat implements vfs.FileDescriptionImpl.Stat.
|
||||
func (sfd *slaveFileDescription) Stat(ctx context.Context, opts vfs.StatOptions) (linux.Statx, error) {
|
||||
func (sfd *replicaFileDescription) Stat(ctx context.Context, opts vfs.StatOptions) (linux.Statx, error) {
|
||||
fs := sfd.vfsfd.VirtualDentry().Mount().Filesystem()
|
||||
return sfd.inode.Stat(ctx, fs, opts)
|
||||
}
|
||||
|
||||
// LockPOSIX implements vfs.FileDescriptionImpl.LockPOSIX.
|
||||
func (sfd *slaveFileDescription) LockPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, start, length uint64, whence int16, block fslock.Blocker) error {
|
||||
func (sfd *replicaFileDescription) LockPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, start, length uint64, whence int16, block fslock.Blocker) error {
|
||||
return sfd.Locks().LockPOSIX(ctx, &sfd.vfsfd, uid, t, start, length, whence, block)
|
||||
}
|
||||
|
||||
// UnlockPOSIX implements vfs.FileDescriptionImpl.UnlockPOSIX.
|
||||
func (sfd *slaveFileDescription) UnlockPOSIX(ctx context.Context, uid fslock.UniqueID, start, length uint64, whence int16) error {
|
||||
func (sfd *replicaFileDescription) UnlockPOSIX(ctx context.Context, uid fslock.UniqueID, start, length uint64, whence int16) error {
|
||||
return sfd.Locks().UnlockPOSIX(ctx, &sfd.vfsfd, uid, start, length, whence)
|
||||
}
|
||||
@@ -36,18 +36,18 @@ type Terminal struct {
|
||||
// this terminal. This field is immutable.
|
||||
masterKTTY *kernel.TTY
|
||||
|
||||
// slaveKTTY contains the controlling process of the slave end of this
|
||||
// replicaKTTY contains the controlling process of the replica end of this
|
||||
// terminal. This field is immutable.
|
||||
slaveKTTY *kernel.TTY
|
||||
replicaKTTY *kernel.TTY
|
||||
}
|
||||
|
||||
func newTerminal(n uint32) *Terminal {
|
||||
termios := linux.DefaultSlaveTermios
|
||||
termios := linux.DefaultReplicaTermios
|
||||
t := Terminal{
|
||||
n: n,
|
||||
ld: newLineDiscipline(termios),
|
||||
masterKTTY: &kernel.TTY{Index: n},
|
||||
slaveKTTY: &kernel.TTY{Index: n},
|
||||
n: n,
|
||||
ld: newLineDiscipline(termios),
|
||||
masterKTTY: &kernel.TTY{Index: n},
|
||||
replicaKTTY: &kernel.TTY{Index: n},
|
||||
}
|
||||
return &t
|
||||
}
|
||||
@@ -113,5 +113,5 @@ func (tm *Terminal) tty(isMaster bool) *kernel.TTY {
|
||||
if isMaster {
|
||||
return tm.masterKTTY
|
||||
}
|
||||
return tm.slaveKTTY
|
||||
return tm.replicaKTTY
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user