From 12319125d6d8717699f61482087006d251f930c8 Mon Sep 17 00:00:00 2001 From: Keith Rarick Date: Tue, 3 May 2011 11:06:58 -0700 Subject: [PATCH] initial --- .gitignore | 4 +++ Makefile | 9 ++++++ README.md | 40 +++++++++++++++++++++++ pty_darwin.go | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++ pty_linux.go | 68 +++++++++++++++++++++++++++++++++++++++ run.go | 33 +++++++++++++++++++ 6 files changed, 243 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 pty_darwin.go create mode 100644 pty_linux.go create mode 100644 run.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f0a99f --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +[568].out +_go* +_test* +_obj diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..139ac85 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +GOROOT ?= $(shell printf 't:;@echo $$(GOROOT)\n' | gomake -f -) +include $(GOROOT)/src/Make.inc + +TARG=github.com/kr/pty +GOFILES=\ + pty_$(GOOS).go\ + run.go\ + +include $(GOROOT)/src/Make.pkg diff --git a/README.md b/README.md new file mode 100644 index 0000000..2ad52eb --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# pty + +Pty is a Go package for using unix pseudo-terminals. + +## Install + + goinstall github.com/kr/pty + +## Example + + package main + + import ( + "fmt" + "github.com/kr/pty" + "io" + "os" + ) + + + func main() { + c, err := pty.Run( + "/bin/grep", + []string{"grep", "--color=auto", "bar"}, + nil, + "", + ) + if err != nil { + panic(err) + } + + go func() { + fmt.Fprintln(c.Stdin, "foo") + fmt.Fprintln(c.Stdin, "bar") + fmt.Fprintln(c.Stdin, "baz") + c.Stdin.Close() + }() + io.Copy(os.Stdout, c.Stdout) + c.Wait(0) + } diff --git a/pty_darwin.go b/pty_darwin.go new file mode 100644 index 0000000..f12e51f --- /dev/null +++ b/pty_darwin.go @@ -0,0 +1,89 @@ +package pty + +import ( + "os" + "syscall" + "unsafe" +) + +const ( + sys_TIOCGPTN = 0x80045430 + sys_TIOCSPTLCK = 0x40045431 +) + + +// Opens a pty and its corresponding tty. +func Open() (pty, tty *os.File, err os.Error) { + p, err := os.Open("/dev/ptmx", os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + + sname, err := ptsname(p) + if err != nil { + return nil, nil, err + } + + err = grantpt(p) + if err != nil { + return nil, nil, err + } + + t, err := os.Open(sname, os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + return p, t, nil +} + +const ( + ptdev1 = "pqrsPQRS" + ptdev2 = "0123456789abcdefghijklmnopqrstuv" +) + +func ptsname(f *os.File) (string, os.Error) { + fi, err := f.Stat() + if err != nil { + return "", err + } + return "/dev/tty" + string([]byte{ + ptdev1[minor(fi.Rdev)/32], + ptdev2[minor(fi.Rdev)%32], + }), nil +} + + +func grantpt(f *os.File) os.Error { + p, err := os.StartProcess("/bin/ptchown", []string{"/bin/ptchown"}, +nil, "", []*os.File{f}) + if err != nil { + return err + } + w, err := p.Wait(0) + if err != nil { + return err + } + if w.Exited() && w.ExitStatus() == 0 { + return nil + } + return os.EACCES +} + + +func ioctl(fd int, cmd uint, data *int) os.Error { + _, _, e := syscall.Syscall( + syscall.SYS_IOCTL, + uintptr(fd), + uintptr(cmd), + uintptr(unsafe.Pointer(data)), + ) + if e != 0 { + return os.ENOTTY + } + return nil +} + + +func minor(d uint64) int { + return int(d & 0xffffffff) +} diff --git a/pty_linux.go b/pty_linux.go new file mode 100644 index 0000000..51fe28a --- /dev/null +++ b/pty_linux.go @@ -0,0 +1,68 @@ +package pty + +import ( + "os" + "strconv" + "syscall" + "unsafe" +) + +const ( + sys_TIOCGPTN = 0x80045430 + sys_TIOCSPTLCK = 0x40045431 +) + + +// Opens a pty and its corresponding tty. +func Open() (pty, tty *os.File, err os.Error) { + p, err := os.Open("/dev/ptmx", os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + + sname, err := ptsname(p) + if err != nil { + return nil, nil, err + } + + err = unlockpt(p) + if err != nil { + return nil, nil, err + } + + t, err := os.Open(sname, os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + return p, t, nil +} + + +func ptsname(f *os.File) (string, os.Error) { + var n int + err := ioctl(f.Fd(), sys_TIOCGPTN, &n) + if err != nil { + return "", err + } + return "/dev/pts/" + strconv.Itoa(n), nil +} + + +func unlockpt(f *os.File) os.Error { + var u int + return ioctl(f.Fd(), sys_TIOCSPTLCK, &u) +} + + +func ioctl(fd int, cmd uint, data *int) os.Error { + _, _, e := syscall.Syscall( + syscall.SYS_IOCTL, + uintptr(fd), + uintptr(cmd), + uintptr(unsafe.Pointer(data)), + ) + if e != 0 { + return os.ENOTTY + } + return nil +} diff --git a/run.go b/run.go new file mode 100644 index 0000000..30063a6 --- /dev/null +++ b/run.go @@ -0,0 +1,33 @@ +package pty + +import ( + "exec" + "os" +) + + +// Run starts a process with its stdin, stdout, and stderr +// connected to a pseudo-terminal tty; +// Stdin and Stdout of the returned exec.Cmd +// are the corresponding pty (Stderr is always nil). +// Arguments name, argv, envv, and dir are passed +// to os.StartProcess unchanged. +func Run(name string, argv, envv []string, dir string) (c *exec.Cmd, err os.Error) { + c = new(exec.Cmd) + var fd [3]*os.File + + c.Stdin, fd[0], err = Open() + if err != nil { + return nil, err + } + fd[1] = fd[0] + fd[2] = fd[0] + c.Stdout = c.Stdin + c.Process, err = os.StartProcess(name, argv, envv, dir, fd[:]) + fd[0].Close() + if err != nil { + c.Stdin.Close() + return nil, err + } + return c, nil +}