1
2
3
4 package stdio
5
6 import (
7 "fmt"
8 "os"
9 "syscall"
10 "unsafe"
11
12 "github.com/pkg/errors"
13 "golang.org/x/sys/unix"
14 )
15
16
17
18 func NewConsole() (*os.File, string, error) {
19 master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
20 if err != nil {
21 return nil, "", errors.Wrap(err, "failed to open master pseudoterminal file")
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
31 if err := os.Chmod(console, 0600); err != nil {
32 return nil, "", errors.Wrap(err, "failed to change permissions on the slave pseudoterminal file")
33 }
34 if err := os.Chown(console, 0, 0); err != nil {
35 return nil, "", errors.Wrap(err, "failed to change ownership on the slave pseudoterminal file")
36 }
37 return master, console, nil
38 }
39
40
41
42 func ResizeConsole(pty *os.File, height, width uint16) error {
43 type consoleSize struct {
44 Height uint16
45 Width uint16
46 x uint16
47 y uint16
48 }
49
50 return ioctl(pty.Fd(), uintptr(unix.TIOCSWINSZ), uintptr(unsafe.Pointer(&consoleSize{Height: height, Width: width})))
51 }
52
53 func ioctl(fd uintptr, flag, data uintptr) error {
54 if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, flag, data); err != 0 {
55 return err
56 }
57 return nil
58 }
59
60
61
62 func ptsname(f *os.File) (string, error) {
63 var n int32
64 if err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))); err != nil {
65 return "", errors.Wrap(err, "ioctl TIOCGPTN failed for ptsname")
66 }
67 return fmt.Sprintf("/dev/pts/%d", n), nil
68 }
69
70
71
72 func unlockpt(f *os.File) error {
73 var u int32
74 if err := ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))); err != nil {
75 return errors.Wrap(err, "ioctl TIOCSPTLCK failed for unlockpt")
76 }
77 return nil
78 }
79
View as plain text