2018-10-19 23:34:09 +00:00
|
|
|
// Copyright 2018 Google LLC
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package tty
|
|
|
|
|
|
|
|
import (
|
|
|
|
"gvisor.googlesource.com/gvisor/pkg/abi/linux"
|
|
|
|
"gvisor.googlesource.com/gvisor/pkg/sentry/arch"
|
|
|
|
"gvisor.googlesource.com/gvisor/pkg/sentry/context"
|
|
|
|
"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
|
|
|
|
"gvisor.googlesource.com/gvisor/pkg/sentry/fs/fsutil"
|
2018-10-20 18:12:26 +00:00
|
|
|
"gvisor.googlesource.com/gvisor/pkg/sentry/unimpl"
|
2018-04-27 17:37:02 +00:00
|
|
|
"gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
|
|
|
|
"gvisor.googlesource.com/gvisor/pkg/syserror"
|
|
|
|
"gvisor.googlesource.com/gvisor/pkg/waiter"
|
|
|
|
)
|
|
|
|
|
|
|
|
// masterInodeOperations are the fs.InodeOperations for the master end of the
|
|
|
|
// Terminal (ptmx file).
|
2018-08-02 17:41:44 +00:00
|
|
|
//
|
|
|
|
// +stateify savable
|
2018-04-27 17:37:02 +00:00
|
|
|
type masterInodeOperations struct {
|
2019-01-15 04:33:29 +00:00
|
|
|
fsutil.SimpleFileInode
|
2018-04-27 17:37:02 +00:00
|
|
|
|
|
|
|
// d is the containing dir.
|
|
|
|
d *dirInodeOperations
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ fs.InodeOperations = (*masterInodeOperations)(nil)
|
|
|
|
|
|
|
|
// newMasterInode creates an Inode for the master end of a terminal.
|
|
|
|
func newMasterInode(ctx context.Context, d *dirInodeOperations, owner fs.FileOwner, p fs.FilePermissions) *fs.Inode {
|
|
|
|
iops := &masterInodeOperations{
|
2019-01-15 04:33:29 +00:00
|
|
|
SimpleFileInode: *fsutil.NewSimpleFileInode(ctx, owner, p, linux.DEVPTS_SUPER_MAGIC),
|
|
|
|
d: d,
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return fs.NewInode(iops, d.msrc, fs.StableAttr{
|
|
|
|
DeviceID: ptsDevice.DeviceID(),
|
|
|
|
// N.B. Linux always uses inode id 2 for ptmx. See
|
|
|
|
// fs/devpts/inode.c:mknod_ptmx.
|
|
|
|
//
|
|
|
|
// TODO: Since ptsDevice must be shared between
|
|
|
|
// different mounts, we must not assign fixed numbers.
|
|
|
|
InodeID: ptsDevice.NextIno(),
|
|
|
|
Type: fs.CharacterDevice,
|
|
|
|
// See fs/devpts/inode.c:devpts_fill_super.
|
|
|
|
BlockSize: 1024,
|
|
|
|
// The PTY master effectively has two different major/minor
|
|
|
|
// device numbers.
|
|
|
|
//
|
|
|
|
// This one is returned by stat for both opened and unopened
|
|
|
|
// instances of this inode.
|
|
|
|
//
|
|
|
|
// When the inode is opened (GetFile), a new device number is
|
|
|
|
// allocated based on major UNIX98_PTY_MASTER_MAJOR and the tty
|
|
|
|
// index as minor number. However, this device number is only
|
|
|
|
// accessible via ioctl(TIOCGDEV) and /proc/TID/stat.
|
|
|
|
DeviceFileMajor: linux.TTYAUX_MAJOR,
|
|
|
|
DeviceFileMinor: linux.PTMX_MINOR,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Release implements fs.InodeOperations.Release.
|
|
|
|
func (mi *masterInodeOperations) Release(ctx context.Context) {
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetFile implements fs.InodeOperations.GetFile.
|
|
|
|
//
|
|
|
|
// It allocates a new terminal.
|
|
|
|
func (mi *masterInodeOperations) GetFile(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
|
|
|
|
t, err := mi.d.allocateTerminal(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return fs.NewFile(ctx, d, flags, &masterFileOperations{
|
|
|
|
d: mi.d,
|
|
|
|
t: t,
|
|
|
|
}), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// masterFileOperations are the fs.FileOperations for the master end of a terminal.
|
2018-08-02 17:41:44 +00:00
|
|
|
//
|
|
|
|
// +stateify savable
|
2018-04-27 17:37:02 +00:00
|
|
|
type masterFileOperations struct {
|
2019-04-11 07:41:42 +00:00
|
|
|
fsutil.FilePipeSeek `state:"nosave"`
|
|
|
|
fsutil.FileNotDirReaddir `state:"nosave"`
|
|
|
|
fsutil.FileNoFsync `state:"nosave"`
|
|
|
|
fsutil.FileNoopFlush `state:"nosave"`
|
|
|
|
fsutil.FileNoMMap `state:"nosave"`
|
|
|
|
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
|
2018-04-27 17:37:02 +00:00
|
|
|
|
|
|
|
// d is the containing dir.
|
|
|
|
d *dirInodeOperations
|
|
|
|
|
|
|
|
// t is the connected Terminal.
|
|
|
|
t *Terminal
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ fs.FileOperations = (*masterFileOperations)(nil)
|
|
|
|
|
|
|
|
// Release implements fs.FileOperations.Release.
|
|
|
|
func (mf *masterFileOperations) Release() {
|
|
|
|
mf.d.masterClose(mf.t)
|
|
|
|
mf.t.DecRef()
|
|
|
|
}
|
|
|
|
|
|
|
|
// EventRegister implements waiter.Waitable.EventRegister.
|
|
|
|
func (mf *masterFileOperations) EventRegister(e *waiter.Entry, mask waiter.EventMask) {
|
2018-08-14 23:21:38 +00:00
|
|
|
mf.t.ld.masterWaiter.EventRegister(e, mask)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// EventUnregister implements waiter.Waitable.EventUnregister.
|
|
|
|
func (mf *masterFileOperations) EventUnregister(e *waiter.Entry) {
|
2018-08-14 23:21:38 +00:00
|
|
|
mf.t.ld.masterWaiter.EventUnregister(e)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Readiness implements waiter.Waitable.Readiness.
|
|
|
|
func (mf *masterFileOperations) Readiness(mask waiter.EventMask) waiter.EventMask {
|
|
|
|
return mf.t.ld.masterReadiness()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read implements fs.FileOperations.Read.
|
|
|
|
func (mf *masterFileOperations) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, _ int64) (int64, error) {
|
|
|
|
return mf.t.ld.outputQueueRead(ctx, dst)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write implements fs.FileOperations.Write.
|
|
|
|
func (mf *masterFileOperations) Write(ctx context.Context, _ *fs.File, src usermem.IOSequence, _ int64) (int64, error) {
|
|
|
|
return mf.t.ld.inputQueueWrite(ctx, src)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ioctl implements fs.FileOperations.Ioctl.
|
|
|
|
func (mf *masterFileOperations) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) {
|
2018-10-20 18:12:26 +00:00
|
|
|
switch cmd := args[1].Uint(); cmd {
|
2018-05-12 00:18:56 +00:00
|
|
|
case linux.FIONREAD: // linux.FIONREAD == linux.TIOCINQ
|
|
|
|
// Get the number of bytes in the output queue read buffer.
|
|
|
|
return 0, mf.t.ld.outputQueueReadSize(ctx, io, args)
|
2018-04-27 17:37:02 +00:00
|
|
|
case linux.TCGETS:
|
|
|
|
// N.B. TCGETS on the master actually returns the configuration
|
|
|
|
// of the slave end.
|
|
|
|
return mf.t.ld.getTermios(ctx, io, args)
|
|
|
|
case linux.TCSETS:
|
|
|
|
// N.B. TCSETS on the master actually affects the configuration
|
|
|
|
// of the slave end.
|
|
|
|
return mf.t.ld.setTermios(ctx, io, args)
|
|
|
|
case linux.TCSETSW:
|
|
|
|
// TODO: This should drain the output queue first.
|
|
|
|
return mf.t.ld.setTermios(ctx, io, args)
|
|
|
|
case linux.TIOCGPTN:
|
|
|
|
_, err := usermem.CopyObjectOut(ctx, io, args[2].Pointer(), uint32(mf.t.n), usermem.IOOpts{
|
|
|
|
AddressSpaceActive: true,
|
|
|
|
})
|
|
|
|
return 0, err
|
|
|
|
case linux.TIOCSPTLCK:
|
|
|
|
// TODO: Implement pty locking. For now just pretend we do.
|
|
|
|
return 0, nil
|
2018-08-27 17:48:02 +00:00
|
|
|
case linux.TIOCGWINSZ:
|
|
|
|
return 0, mf.t.ld.windowSize(ctx, io, args)
|
|
|
|
case linux.TIOCSWINSZ:
|
|
|
|
return 0, mf.t.ld.setWindowSize(ctx, io, args)
|
2018-04-27 17:37:02 +00:00
|
|
|
default:
|
2018-10-20 18:12:26 +00:00
|
|
|
maybeEmitUnimplementedEvent(ctx, cmd)
|
2018-04-27 17:37:02 +00:00
|
|
|
return 0, syserror.ENOTTY
|
|
|
|
}
|
|
|
|
}
|
2018-10-20 18:12:26 +00:00
|
|
|
|
|
|
|
// maybeEmitUnimplementedEvent emits unimplemented event if cmd is valid.
|
|
|
|
func maybeEmitUnimplementedEvent(ctx context.Context, cmd uint32) {
|
|
|
|
switch cmd {
|
|
|
|
case linux.TCGETS,
|
|
|
|
linux.TCSETS,
|
|
|
|
linux.TCSETSW,
|
|
|
|
linux.TCSETSF,
|
|
|
|
linux.TIOCGPGRP,
|
|
|
|
linux.TIOCSPGRP,
|
|
|
|
linux.TIOCGWINSZ,
|
|
|
|
linux.TIOCSWINSZ,
|
|
|
|
linux.TIOCSETD,
|
|
|
|
linux.TIOCSBRK,
|
|
|
|
linux.TIOCCBRK,
|
|
|
|
linux.TCSBRK,
|
|
|
|
linux.TCSBRKP,
|
|
|
|
linux.TIOCSTI,
|
|
|
|
linux.TIOCCONS,
|
|
|
|
linux.FIONBIO,
|
|
|
|
linux.TIOCEXCL,
|
|
|
|
linux.TIOCNXCL,
|
|
|
|
linux.TIOCGEXCL,
|
|
|
|
linux.TIOCNOTTY,
|
|
|
|
linux.TIOCSCTTY,
|
|
|
|
linux.TIOCGSID,
|
|
|
|
linux.TIOCGETD,
|
|
|
|
linux.TIOCVHANGUP,
|
|
|
|
linux.TIOCGDEV,
|
|
|
|
linux.TIOCMGET,
|
|
|
|
linux.TIOCMSET,
|
|
|
|
linux.TIOCMBIC,
|
|
|
|
linux.TIOCMBIS,
|
|
|
|
linux.TIOCGICOUNT,
|
|
|
|
linux.TCFLSH,
|
|
|
|
linux.TIOCSSERIAL,
|
|
|
|
linux.TIOCGPTPEER:
|
|
|
|
|
|
|
|
unimpl.EmitUnimplementedEvent(ctx)
|
|
|
|
}
|
|
|
|
}
|