mirror of https://github.com/liamg/aminal.git
switch to nice pty library
This commit is contained in:
parent
59a7ddb081
commit
f185f655d1
|
@ -35,6 +35,12 @@
|
||||||
]
|
]
|
||||||
revision = "e2365dfdc4a05e4b8299a783240d4a7d5a65d4e4"
|
revision = "e2365dfdc4a05e4b8299a783240d4a7d5a65d4e4"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/kr/pty"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "fa756f09eeb418bf1cc6268c66ceaad9bb98f598"
|
||||||
|
version = "v1.1.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/liamg/glfont"
|
name = "github.com/liamg/glfont"
|
||||||
|
@ -99,6 +105,6 @@
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "0b3960a1d15048ccc49c41464fdb2346062f791ca53d614d4535d2930d4969f5"
|
inputs-digest = "18e6b85742181970b4dbe1aae2d74b033fbca809df825b8a03c8e3ba6b5ba29c"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|
22
main.go
22
main.go
|
@ -5,10 +5,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/kr/pty"
|
||||||
|
"github.com/riywo/loginshell"
|
||||||
"gitlab.com/liamg/raft/config"
|
"gitlab.com/liamg/raft/config"
|
||||||
"gitlab.com/liamg/raft/gui"
|
"gitlab.com/liamg/raft/gui"
|
||||||
"gitlab.com/liamg/raft/pty"
|
|
||||||
"gitlab.com/liamg/raft/terminal"
|
"gitlab.com/liamg/raft/terminal"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -94,11 +97,26 @@ func main() {
|
||||||
defer logger.Sync()
|
defer logger.Sync()
|
||||||
|
|
||||||
logger.Infof("Allocating pty...")
|
logger.Infof("Allocating pty...")
|
||||||
pty, err := pty.NewPtyWithShell()
|
pty, tty, err := pty.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatalf("Failed to allocate pty: %s", err)
|
logger.Fatalf("Failed to allocate pty: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shellStr, err := loginshell.Shell()
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("Failed to ascertain your shell: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
shell := exec.Command(shellStr)
|
||||||
|
shell.Stdout = tty
|
||||||
|
shell.Stdin = tty
|
||||||
|
shell.Stderr = tty
|
||||||
|
shell.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
|
||||||
|
if err := shell.Start(); err != nil {
|
||||||
|
pty.Close()
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
logger.Infof("Creating terminal...")
|
logger.Infof("Creating terminal...")
|
||||||
terminal := terminal.New(pty, logger, conf)
|
terminal := terminal.New(pty, logger, conf)
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ func open() (*os.File, *os.File, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ptsName, err := ptsname(pty)
|
ptsName, err := ptsname(pty)
|
||||||
panic(ptsName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[568].out
|
||||||
|
_go*
|
||||||
|
_test*
|
||||||
|
_obj
|
|
@ -0,0 +1,23 @@
|
||||||
|
Copyright (c) 2011 Keith Rarick
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated
|
||||||
|
documentation files (the "Software"), to deal in the
|
||||||
|
Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute,
|
||||||
|
sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall
|
||||||
|
be included in all copies or substantial portions of the
|
||||||
|
Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
||||||
|
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||||
|
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||||
|
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,100 @@
|
||||||
|
# pty
|
||||||
|
|
||||||
|
Pty is a Go package for using unix pseudo-terminals.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
go get github.com/kr/pty
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
### Command
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kr/pty"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
c := exec.Command("grep", "--color=auto", "bar")
|
||||||
|
f, err := pty.Start(c)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
f.Write([]byte("foo\n"))
|
||||||
|
f.Write([]byte("bar\n"))
|
||||||
|
f.Write([]byte("baz\n"))
|
||||||
|
f.Write([]byte{4}) // EOT
|
||||||
|
}()
|
||||||
|
io.Copy(os.Stdout, f)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Shell
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/kr/pty"
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func test() error {
|
||||||
|
// Create arbitrary command.
|
||||||
|
c := exec.Command("bash")
|
||||||
|
|
||||||
|
// Start the command with a pty.
|
||||||
|
ptmx, err := pty.Start(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Make sure to close the pty at the end.
|
||||||
|
defer func() { _ = ptmx.Close() }() // Best effort.
|
||||||
|
|
||||||
|
// Handle pty size.
|
||||||
|
ch := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(ch, syscall.SIGWINCH)
|
||||||
|
go func() {
|
||||||
|
for range ch {
|
||||||
|
if err := pty.InheritSize(os.Stdin, ptmx); err != nil {
|
||||||
|
log.Printf("error resizing pty: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
ch <- syscall.SIGWINCH // Initial resize.
|
||||||
|
|
||||||
|
// Set stdin in raw mode.
|
||||||
|
oldState, err := terminal.MakeRaw(int(os.Stdin.Fd()))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer func() { _ = terminal.Restore(int(os.Stdin.Fd()), oldState) }() // Best effort.
|
||||||
|
|
||||||
|
// Copy stdin to the pty and the pty to stdout.
|
||||||
|
go func() { _, _ = io.Copy(ptmx, os.Stdin) }()
|
||||||
|
_, _ = io.Copy(os.Stdout, ptmx)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := test(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Package pty provides functions for working with Unix terminals.
|
||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrUnsupported is returned if a function is not
|
||||||
|
// available on the current platform.
|
||||||
|
var ErrUnsupported = errors.New("unsupported")
|
||||||
|
|
||||||
|
// Opens a pty and its corresponding tty.
|
||||||
|
func Open() (pty, tty *os.File, err error) {
|
||||||
|
return open()
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
func ioctl(fd, cmd, ptr uintptr) error {
|
||||||
|
_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr)
|
||||||
|
if e != 0 {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
// +build darwin dragonfly freebsd netbsd openbsd
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
// from <sys/ioccom.h>
|
||||||
|
const (
|
||||||
|
_IOC_VOID uintptr = 0x20000000
|
||||||
|
_IOC_OUT uintptr = 0x40000000
|
||||||
|
_IOC_IN uintptr = 0x80000000
|
||||||
|
_IOC_IN_OUT uintptr = _IOC_OUT | _IOC_IN
|
||||||
|
_IOC_DIRMASK = _IOC_VOID | _IOC_OUT | _IOC_IN
|
||||||
|
|
||||||
|
_IOC_PARAM_SHIFT = 13
|
||||||
|
_IOC_PARAM_MASK = (1 << _IOC_PARAM_SHIFT) - 1
|
||||||
|
)
|
||||||
|
|
||||||
|
func _IOC_PARM_LEN(ioctl uintptr) uintptr {
|
||||||
|
return (ioctl >> 16) & _IOC_PARAM_MASK
|
||||||
|
}
|
||||||
|
|
||||||
|
func _IOC(inout uintptr, group byte, ioctl_num uintptr, param_len uintptr) uintptr {
|
||||||
|
return inout | (param_len&_IOC_PARAM_MASK)<<16 | uintptr(group)<<8 | ioctl_num
|
||||||
|
}
|
||||||
|
|
||||||
|
func _IO(group byte, ioctl_num uintptr) uintptr {
|
||||||
|
return _IOC(_IOC_VOID, group, ioctl_num, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _IOR(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
|
||||||
|
return _IOC(_IOC_OUT, group, ioctl_num, param_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _IOW(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
|
||||||
|
return _IOC(_IOC_IN, group, ioctl_num, param_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _IOWR(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
|
||||||
|
return _IOC(_IOC_IN_OUT, group, ioctl_num, param_len)
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
GOOSARCH="${GOOS}_${GOARCH}"
|
||||||
|
case "$GOOSARCH" in
|
||||||
|
_* | *_ | _)
|
||||||
|
echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
GODEFS="go tool cgo -godefs"
|
||||||
|
|
||||||
|
$GODEFS types.go |gofmt > ztypes_$GOARCH.go
|
||||||
|
|
||||||
|
case $GOOS in
|
||||||
|
freebsd|dragonfly|openbsd)
|
||||||
|
$GODEFS types_$GOOS.go |gofmt > ztypes_$GOOSARCH.go
|
||||||
|
;;
|
||||||
|
esac
|
|
@ -0,0 +1,65 @@
|
||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func open() (pty, tty *os.File, err error) {
|
||||||
|
pFD, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_CLOEXEC, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
p := os.NewFile(uintptr(pFD), "/dev/ptmx")
|
||||||
|
// In case of error after this point, make sure we close the ptmx fd.
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = p.Close() // Best effort.
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
sname, err := ptsname(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := grantpt(p); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := unlockpt(p); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := os.OpenFile(sname, os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return p, t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ptsname(f *os.File) (string, error) {
|
||||||
|
n := make([]byte, _IOC_PARM_LEN(syscall.TIOCPTYGNAME))
|
||||||
|
|
||||||
|
err := ioctl(f.Fd(), syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0])))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, c := range n {
|
||||||
|
if c == 0 {
|
||||||
|
return string(n[:i]), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", errors.New("TIOCPTYGNAME string not NUL-terminated")
|
||||||
|
}
|
||||||
|
|
||||||
|
func grantpt(f *os.File) error {
|
||||||
|
return ioctl(f.Fd(), syscall.TIOCPTYGRANT, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unlockpt(f *os.File) error {
|
||||||
|
return ioctl(f.Fd(), syscall.TIOCPTYUNLK, 0)
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// same code as pty_darwin.go
|
||||||
|
func open() (pty, tty *os.File, err error) {
|
||||||
|
p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
// In case of error after this point, make sure we close the ptmx fd.
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = p.Close() // Best effort.
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
sname, err := ptsname(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := grantpt(p); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := unlockpt(p); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := os.OpenFile(sname, os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return p, t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func grantpt(f *os.File) error {
|
||||||
|
_, err := isptmaster(f.Fd())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func unlockpt(f *os.File) error {
|
||||||
|
_, err := isptmaster(f.Fd())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func isptmaster(fd uintptr) (bool, error) {
|
||||||
|
err := ioctl(fd, syscall.TIOCISPTMASTER, 0)
|
||||||
|
return err == nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
emptyFiodgnameArg fiodgnameArg
|
||||||
|
ioctl_FIODNAME = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg))
|
||||||
|
)
|
||||||
|
|
||||||
|
func ptsname(f *os.File) (string, error) {
|
||||||
|
name := make([]byte, _C_SPECNAMELEN)
|
||||||
|
fa := fiodgnameArg{Name: (*byte)(unsafe.Pointer(&name[0])), Len: _C_SPECNAMELEN, Pad_cgo_0: [4]byte{0, 0, 0, 0}}
|
||||||
|
|
||||||
|
err := ioctl(f.Fd(), ioctl_FIODNAME, uintptr(unsafe.Pointer(&fa)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, c := range name {
|
||||||
|
if c == 0 {
|
||||||
|
s := "/dev/" + string(name[:i])
|
||||||
|
return strings.Replace(s, "ptm", "pts", -1), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", errors.New("TIOCPTYGNAME string not NUL-terminated")
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func posixOpenpt(oflag int) (fd int, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall(syscall.SYS_POSIX_OPENPT, uintptr(oflag), 0, 0)
|
||||||
|
fd = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = e1
|
||||||
|
}
|
||||||
|
return fd, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func open() (pty, tty *os.File, err error) {
|
||||||
|
fd, err := posixOpenpt(syscall.O_RDWR | syscall.O_CLOEXEC)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
p := os.NewFile(uintptr(fd), "/dev/pts")
|
||||||
|
// In case of error after this point, make sure we close the pts fd.
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = p.Close() // Best effort.
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
sname, err := ptsname(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := os.OpenFile("/dev/"+sname, os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return p, t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isptmaster(fd uintptr) (bool, error) {
|
||||||
|
err := ioctl(fd, syscall.TIOCPTMASTER, 0)
|
||||||
|
return err == nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
emptyFiodgnameArg fiodgnameArg
|
||||||
|
ioctlFIODGNAME = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg))
|
||||||
|
)
|
||||||
|
|
||||||
|
func ptsname(f *os.File) (string, error) {
|
||||||
|
master, err := isptmaster(f.Fd())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !master {
|
||||||
|
return "", syscall.EINVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
const n = _C_SPECNAMELEN + 1
|
||||||
|
var (
|
||||||
|
buf = make([]byte, n)
|
||||||
|
arg = fiodgnameArg{Len: n, Buf: (*byte)(unsafe.Pointer(&buf[0]))}
|
||||||
|
)
|
||||||
|
if err := ioctl(f.Fd(), ioctlFIODGNAME, uintptr(unsafe.Pointer(&arg))); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, c := range buf {
|
||||||
|
if c == 0 {
|
||||||
|
return string(buf[:i]), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", errors.New("FIODGNAME string not NUL-terminated")
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func open() (pty, tty *os.File, err error) {
|
||||||
|
p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
// In case of error after this point, make sure we close the ptmx fd.
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = p.Close() // Best effort.
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
sname, err := ptsname(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := unlockpt(p); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return p, t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ptsname(f *os.File) (string, error) {
|
||||||
|
var n _C_uint
|
||||||
|
err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return "/dev/pts/" + strconv.Itoa(int(n)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unlockpt(f *os.File) error {
|
||||||
|
var u _C_int
|
||||||
|
// use TIOCSPTLCK with a zero valued arg to clear the slave pty lock
|
||||||
|
return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func open() (pty, tty *os.File, err error) {
|
||||||
|
/*
|
||||||
|
* from ptm(4):
|
||||||
|
* The PTMGET command allocates a free pseudo terminal, changes its
|
||||||
|
* ownership to the caller, revokes the access privileges for all previous
|
||||||
|
* users, opens the file descriptors for the master and slave devices and
|
||||||
|
* returns them to the caller in struct ptmget.
|
||||||
|
*/
|
||||||
|
|
||||||
|
p, err := os.OpenFile("/dev/ptm", os.O_RDWR|syscall.O_CLOEXEC, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
defer p.Close()
|
||||||
|
|
||||||
|
var ptm ptmget
|
||||||
|
if err := ioctl(p.Fd(), uintptr(ioctl_PTMGET), uintptr(unsafe.Pointer(&ptm))); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pty = os.NewFile(uintptr(ptm.Cfd), "/dev/ptm")
|
||||||
|
tty = os.NewFile(uintptr(ptm.Sfd), "/dev/ptm")
|
||||||
|
|
||||||
|
return pty, tty, nil
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
// +build !linux,!darwin,!freebsd,!dragonfly,!openbsd
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func open() (pty, tty *os.File, err error) {
|
||||||
|
return nil, nil, ErrUnsupported
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Start assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout,
|
||||||
|
// and c.Stderr, calls c.Start, and returns the File of the tty's
|
||||||
|
// corresponding pty.
|
||||||
|
func Start(c *exec.Cmd) (pty *os.File, err error) {
|
||||||
|
pty, tty, err := Open()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer tty.Close()
|
||||||
|
c.Stdout = tty
|
||||||
|
c.Stdin = tty
|
||||||
|
c.Stderr = tty
|
||||||
|
if c.SysProcAttr == nil {
|
||||||
|
c.SysProcAttr = &syscall.SysProcAttr{}
|
||||||
|
}
|
||||||
|
c.SysProcAttr.Setctty = true
|
||||||
|
c.SysProcAttr.Setsid = true
|
||||||
|
err = c.Start()
|
||||||
|
if err != nil {
|
||||||
|
pty.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pty, err
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
type (
|
||||||
|
_C_int C.int
|
||||||
|
_C_uint C.uint
|
||||||
|
)
|
|
@ -0,0 +1,17 @@
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
/*
|
||||||
|
#define _KERNEL
|
||||||
|
#include <sys/conf.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/filio.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
const (
|
||||||
|
_C_SPECNAMELEN = C.SPECNAMELEN /* max length of devicename */
|
||||||
|
)
|
||||||
|
|
||||||
|
type fiodgnameArg C.struct_fiodname_args
|
|
@ -0,0 +1,15 @@
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/filio.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
const (
|
||||||
|
_C_SPECNAMELEN = C.SPECNAMELEN /* max length of devicename */
|
||||||
|
)
|
||||||
|
|
||||||
|
type fiodgnameArg C.struct_fiodgname_arg
|
|
@ -0,0 +1,14 @@
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/tty.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
type ptmget C.struct_ptmget
|
||||||
|
|
||||||
|
var ioctl_PTMGET = C.PTMGET
|
|
@ -0,0 +1,64 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InheritSize applies the terminal size of master to slave. This should be run
|
||||||
|
// in a signal handler for syscall.SIGWINCH to automatically resize the slave when
|
||||||
|
// the master receives a window size change notification.
|
||||||
|
func InheritSize(master, slave *os.File) error {
|
||||||
|
size, err := GetsizeFull(master)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = Setsize(slave, size)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setsize resizes t to s.
|
||||||
|
func Setsize(t *os.File, ws *Winsize) error {
|
||||||
|
return windowRectCall(ws, t.Fd(), syscall.TIOCSWINSZ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetsizeFull returns the full terminal size description.
|
||||||
|
func GetsizeFull(t *os.File) (size *Winsize, err error) {
|
||||||
|
var ws Winsize
|
||||||
|
err = windowRectCall(&ws, t.Fd(), syscall.TIOCGWINSZ)
|
||||||
|
return &ws, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getsize returns the number of rows (lines) and cols (positions
|
||||||
|
// in each line) in terminal t.
|
||||||
|
func Getsize(t *os.File) (rows, cols int, err error) {
|
||||||
|
ws, err := GetsizeFull(t)
|
||||||
|
return int(ws.Rows), int(ws.Cols), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Winsize describes the terminal size.
|
||||||
|
type Winsize struct {
|
||||||
|
Rows uint16 // ws_row: Number of rows (in cells)
|
||||||
|
Cols uint16 // ws_col: Number of columns (in cells)
|
||||||
|
X uint16 // ws_xpixel: Width in pixels
|
||||||
|
Y uint16 // ws_ypixel: Height in pixels
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowRectCall(ws *Winsize, fd, a2 uintptr) error {
|
||||||
|
_, _, errno := syscall.Syscall(
|
||||||
|
syscall.SYS_IOCTL,
|
||||||
|
fd,
|
||||||
|
a2,
|
||||||
|
uintptr(unsafe.Pointer(ws)),
|
||||||
|
)
|
||||||
|
if errno != 0 {
|
||||||
|
return syscall.Errno(errno)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
type (
|
||||||
|
_C_int int32
|
||||||
|
_C_uint uint32
|
||||||
|
)
|
|
@ -0,0 +1,9 @@
|
||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
type (
|
||||||
|
_C_int int32
|
||||||
|
_C_uint uint32
|
||||||
|
)
|
|
@ -0,0 +1,9 @@
|
||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
type (
|
||||||
|
_C_int int32
|
||||||
|
_C_uint uint32
|
||||||
|
)
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types.go
|
||||||
|
|
||||||
|
// +build arm64
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
type (
|
||||||
|
_C_int int32
|
||||||
|
_C_uint uint32
|
||||||
|
)
|
|
@ -0,0 +1,14 @@
|
||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types_dragonfly.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
const (
|
||||||
|
_C_SPECNAMELEN = 0x3f
|
||||||
|
)
|
||||||
|
|
||||||
|
type fiodgnameArg struct {
|
||||||
|
Name *byte
|
||||||
|
Len uint32
|
||||||
|
Pad_cgo_0 [4]byte
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types_freebsd.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
const (
|
||||||
|
_C_SPECNAMELEN = 0x3f
|
||||||
|
)
|
||||||
|
|
||||||
|
type fiodgnameArg struct {
|
||||||
|
Len int32
|
||||||
|
Buf *byte
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types_freebsd.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
const (
|
||||||
|
_C_SPECNAMELEN = 0x3f
|
||||||
|
)
|
||||||
|
|
||||||
|
type fiodgnameArg struct {
|
||||||
|
Len int32
|
||||||
|
Pad_cgo_0 [4]byte
|
||||||
|
Buf *byte
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types_freebsd.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
const (
|
||||||
|
_C_SPECNAMELEN = 0x3f
|
||||||
|
)
|
||||||
|
|
||||||
|
type fiodgnameArg struct {
|
||||||
|
Len int32
|
||||||
|
Buf *byte
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types.go
|
||||||
|
|
||||||
|
// +build linux
|
||||||
|
// +build mips mipsle mips64 mips64le
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
type (
|
||||||
|
_C_int int32
|
||||||
|
_C_uint uint32
|
||||||
|
)
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types_openbsd.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
type ptmget struct {
|
||||||
|
Cfd int32
|
||||||
|
Sfd int32
|
||||||
|
Cn [16]int8
|
||||||
|
Sn [16]int8
|
||||||
|
}
|
||||||
|
|
||||||
|
var ioctl_PTMGET = 0x40287401
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types_openbsd.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
type ptmget struct {
|
||||||
|
Cfd int32
|
||||||
|
Sfd int32
|
||||||
|
Cn [16]int8
|
||||||
|
Sn [16]int8
|
||||||
|
}
|
||||||
|
|
||||||
|
var ioctl_PTMGET = 0x40287401
|
|
@ -0,0 +1,11 @@
|
||||||
|
// +build ppc64
|
||||||
|
|
||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
type (
|
||||||
|
_C_int int32
|
||||||
|
_C_uint uint32
|
||||||
|
)
|
|
@ -0,0 +1,11 @@
|
||||||
|
// +build ppc64le
|
||||||
|
|
||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
type (
|
||||||
|
_C_int int32
|
||||||
|
_C_uint uint32
|
||||||
|
)
|
|
@ -0,0 +1,11 @@
|
||||||
|
// +build s390x
|
||||||
|
|
||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
type (
|
||||||
|
_C_int int32
|
||||||
|
_C_uint uint32
|
||||||
|
)
|
|
@ -219,7 +219,7 @@ func (f *Font) Height(scale float32, text string) float32 {
|
||||||
baseHeight = height
|
baseHeight = height
|
||||||
height = 0
|
height = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
//skip runes that are not in font chacter range
|
//skip runes that are not in font chacter range
|
||||||
if int(runeIndex)-int(lowChar) > len(f.fontChar) || runeIndex < lowChar {
|
if int(runeIndex)-int(lowChar) > len(f.fontChar) || runeIndex < lowChar {
|
||||||
continue
|
continue
|
||||||
|
@ -229,7 +229,7 @@ func (f *Font) Height(scale float32, text string) float32 {
|
||||||
ch := f.fontChar[runeIndex-lowChar]
|
ch := f.fontChar[runeIndex-lowChar]
|
||||||
|
|
||||||
// Now advance cursors for next glyph (note that advance is number of 1/64 pixels)
|
// Now advance cursors for next glyph (note that advance is number of 1/64 pixels)
|
||||||
if float32(ch.height)*scale > height {
|
if float32(ch.height) * scale > height {
|
||||||
height = float32(ch.height) * scale
|
height = float32(ch.height) * scale
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue