1
0
mirror of https://github.com/lxc/incus.git synced 2026-02-05 18:45:46 +01:00
Files
incus/internal/linux/pty.go
2025-05-23 01:45:31 -04:00

145 lines
3.5 KiB
Go

package linux
import (
"errors"
"fmt"
"os"
"unsafe"
"golang.org/x/sys/unix"
"github.com/lxc/incus/v6/shared/revert"
)
// OpenPtyInDevpts creates a new PTS pair, configures them and returns them.
func OpenPtyInDevpts(devpts_fd int, uid, gid int64) (*os.File, *os.File, error) {
reverter := revert.New()
defer reverter.Fail()
var fd int
var ptx *os.File
var err error
// Create a PTS pair.
if devpts_fd >= 0 {
fd, err = unix.Openat(devpts_fd, "ptmx", unix.O_RDWR|unix.O_CLOEXEC|unix.O_NOCTTY, 0)
} else {
fd, err = unix.Openat(-1, "/dev/ptmx", unix.O_RDWR|unix.O_CLOEXEC|unix.O_NOCTTY, 0)
}
if err != nil {
return nil, nil, err
}
ptx = os.NewFile(uintptr(fd), "/dev/pts/ptmx")
reverter.Add(func() { _ = ptx.Close() })
// Unlock the ptx and pty.
val := 0
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(ptx.Fd()), unix.TIOCSPTLCK, uintptr(unsafe.Pointer(&val)))
if errno != 0 {
return nil, nil, unix.Errno(errno)
}
var pty *os.File
ptyFd, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(ptx.Fd()), unix.TIOCGPTPEER, uintptr(unix.O_NOCTTY|unix.O_CLOEXEC|os.O_RDWR))
// We can only fallback to looking up the fd in /dev/pts when we aren't dealing with the container's devpts instance.
if errno == 0 {
// Get the pty side.
id := 0
_, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(ptx.Fd()), unix.TIOCGPTN, uintptr(unsafe.Pointer(&id)))
if errno != 0 {
return nil, nil, unix.Errno(errno)
}
pty = os.NewFile(ptyFd, fmt.Sprintf("/dev/pts/%d", id))
} else {
if devpts_fd >= 0 {
return nil, nil, errors.New("TIOCGPTPEER required but not available")
}
// Get the pty side.
id := 0
_, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(ptx.Fd()), unix.TIOCGPTN, uintptr(unsafe.Pointer(&id)))
if errno != 0 {
return nil, nil, unix.Errno(errno)
}
// Open the pty.
pty, err = os.OpenFile(fmt.Sprintf("/dev/pts/%d", id), unix.O_NOCTTY|unix.O_CLOEXEC|os.O_RDWR, 0)
if err != nil {
return nil, nil, err
}
}
reverter.Add(func() { _ = pty.Close() })
// Configure both sides
for _, entry := range []*os.File{ptx, pty} {
// Get termios.
t, err := unix.IoctlGetTermios(int(entry.Fd()), unix.TCGETS)
if err != nil {
return nil, nil, err
}
// Set flags.
t.Cflag |= unix.IMAXBEL
t.Cflag |= unix.IUTF8
t.Cflag |= unix.BRKINT
t.Cflag |= unix.IXANY
t.Cflag |= unix.HUPCL
// Set termios.
err = unix.IoctlSetTermios(int(entry.Fd()), unix.TCSETS, t)
if err != nil {
return nil, nil, err
}
// Set the default window size.
sz := &unix.Winsize{
Col: 80,
Row: 25,
}
err = unix.IoctlSetWinsize(int(entry.Fd()), unix.TIOCSWINSZ, sz)
if err != nil {
return nil, nil, err
}
// Set CLOEXEC.
_, _, errno = unix.Syscall(unix.SYS_FCNTL, uintptr(entry.Fd()), unix.F_SETFD, unix.FD_CLOEXEC)
if errno != 0 {
return nil, nil, unix.Errno(errno)
}
}
// Fix the ownership of the pty side.
err = unix.Fchown(int(pty.Fd()), int(uid), int(gid))
if err != nil {
return nil, nil, err
}
reverter.Success()
return ptx, pty, nil
}
// OpenPty creates a new PTS pair, configures them and returns them.
func OpenPty(uid, gid int64) (*os.File, *os.File, error) {
return OpenPtyInDevpts(-1, uid, gid)
}
// SetPtySize issues the correct ioctl to resize a pty.
func SetPtySize(fd int, width int, height int) (err error) {
var dimensions [4]uint16
dimensions[0] = uint16(height)
dimensions[1] = uint16(width)
_, _, errno := unix.Syscall6(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.TIOCSWINSZ), uintptr(unsafe.Pointer(&dimensions)), 0, 0, 0)
if errno != 0 {
return errno
}
return nil
}