Coder Social home page Coder Social logo

pty's Introduction

pty

Pty is a Go package for using unix pseudo-terminals.

Install

go get github.com/creack/pty

Examples

Note that those examples are for demonstration purpose only, to showcase how to use the library. They are not meant to be used in any kind of production environment. If you want to set deadlines to work and Close() interrupting Read() on the returned *os.File, you will need to call syscall.SetNonblock manually.

Command

package main

import (
	"io"
	"os"
	"os/exec"

	"github.com/creack/pty"
)

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

package main

import (
        "io"
        "log"
        "os"
        "os/exec"
        "os/signal"
        "syscall"

        "github.com/creack/pty"
        "golang.org/x/term"
)

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.
        defer func() { signal.Stop(ch); close(ch) }() // Cleanup signals when done.

        // Set stdin in raw mode.
        oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
        if err != nil {
                panic(err)
        }
        defer func() { _ = term.Restore(int(os.Stdin.Fd()), oldState) }() // Best effort.

        // Copy stdin to the pty and the pty to stdout.
        // NOTE: The goroutine will keep reading until the next keystroke before returning.
        go func() { _, _ = io.Copy(ptmx, os.Stdin) }()
        _, _ = io.Copy(os.Stdout, ptmx)

        return nil
}

func main() {
        if err := test(); err != nil {
                log.Fatal(err)
        }
}

pty's People

Contributors

4a6f656c avatar abner-chenc avatar bkcsoft avatar creack avatar ddevault avatar derekmarcotte avatar fazalmajid avatar frassle avatar gcla avatar hqhq avatar ibuclaw avatar jonasi avatar jonathanlogan avatar jonboulle avatar kr avatar lenoil98 avatar matoro avatar mneumann avatar n2vi avatar nwtgck avatar paulzhol avatar phy1729 avatar sherjilozair avatar sio avatar thajeztah avatar virtuald avatar weidideng avatar whorfin avatar xaionaro avatar yoheiueda avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pty's Issues

Defunct child processes on Linux

When the parent process lives longer than the child processes the child processes stay around as defunct. If you modify the example in the README.md and add the following line at the end of the main() function you will see the defunct grep process until the parent finishes:

<-time.After(60*time.Second)

Bash does not detect a TTY when using pty

I'm trying to use pty to start a bash shell in a way that will allow me to pass the --rcfile flag. Because Bash in its infinite wisdom decided not to respect --rcfile unless you're in a TTY.

Problem is that even when using pty it does not detect a tty.

This is the pty specific code I've written (keep in mind this is proof of concept code)

func (p *Process) start() error {
	var err error
	if p.pty, err = pty.Start(p.cmd); err != nil {
		return err
	}

	go func() {
		_, err := io.Copy(p.cmd.Stdout, p.pty)
		if err != nil {
			panic(fmt.Sprintf("Error while copying stdout: %v", err))
		}
		_, err = io.Copy(p.pty, p.cmd.Stdin)
		if err != nil {
			panic(fmt.Sprintf("Error while copying stdin: %v", err))
		}
	}()

	return nil
}

Keep in mind the above is used to basically run bash --rcfile /some/file. Once started I use stdin to send it additional commands. In this case I've sent it the "tty" command and it returns not a tty.

Any idea what I might be missing, or does this package not fully set up a tty?

Thanks

pty.start()

why i cat use pty.start()?it point out that "undefined: pty.Start"

No longer builds on OS X

Looks like latest ARM64 merge has broken things for me on OS X and Linux:

$ go get -u github.com/cespare/reflex
# github.com/kr/pty
../../../github.com/kr/pty/ztypes_arm64.go:7: _C_int redeclared in this block
    previous declaration at ../../../github.com/kr/pty/ztypes_amd64.go:7
../../../github.com/kr/pty/ztypes_arm64.go:8: _C_uint redeclared in this block
    previous declaration at ../../../github.com/kr/pty/ztypes_amd64.go:8

Please release v1.1.5

Following the discussion in golang/go#32527 (comment), it appears that kr@b6e1bdd is required in order to work with pty using Go tip.

Please can I therefore ask you to tag v1.1.5?

For now, I can use master.

But when go1.13beta1 is released, however, an official semver version will be more convenient for people whose setup will be broken.

exec.Command receive context

Hello, i am using pty and make a shellserver when http server is starting by
c := exec.Command("bash", "-l")
And is there a way to send context to it on each request?

For example, I open two web page, sending ?item=1 and ?item=2 separately, and I'd like one termimal has an ENV as ITEM=1 and the other has ITEM=2.

I think making a new shellserver on each request is ok, but I need to destory it when request done.

Thank you.

getting inputs

I wanted to use this to run a ssh -t with a top command. The problem is I need to line up further interactive inputs - like 'm', 'h' for help, 'q' for quit etc. that top command needs for example. Here is my code -

c := exec.Command("ssh", "-t", "some_ssh_link", "'top'")
f, err := pty.Start(c)
if err != nil {
	panic(err)
}
io.Copy(os.Stdout, f)

Any suggestions? It can build up terminal as expected and refreshes as you'd expect but I need to line up os.Stdin with top's input.

how to set timeout in the Pty

I want to set a timeout in the Pty, there is a way to set a timeout in the Conn.net by SetReadDeadline, but the pty is a type of *os.File. So how to set the timeout?

i want to create a stream instead of socket connection? but i failed, Is this feasible?

what should i do that i do not want to use "time.Sleep(1 * time.Second)"

package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/kr/pty"
)

func main() {

	c := exec.Command("/bin/bash", "-l")
	c.Env = append(os.Environ(), "TERM=xterm")
	tty, _ := pty.Start(c)
	sr := strings.NewReader("ls\n")

	ch := make(chan int, 0)

	go func() {
		for {
			read, _ := io.Copy(tty, sr)
			if read > 0 {
				ch <- 1
				fmt.Println("1111111111111111111111111")
				break
			}
		}
	}()

	go func() {
		for {
			select {
			case <-ch:
				time.Sleep(1 * time.Second)
				buf := make([]byte, 1024)
				tty.Read(buf)
				fmt.Println("22222222222222222222222222")
				fmt.Println(string(buf))
			case <-time.After(2 * time.Second):
				continue
			}
		}

	}()

	for {

	}
}


terminal size != terminal size

Hi

I'm having an issue where processes started via pty don't pick up my terminal dimensions. This causes issues with tools like vi and top (basically anything that draws a UI display based around the terminal size) to only use a subset of the parent terminal screen in the top left corner. It's particularly troublesome with tools that don't redraw the entire terminal, such as with vi.

Apologies for the vagueness of this issue, in truth I'm not even sure if it's an issue caused by pty specifically, but it certainly affects processes started with pty. The issue is I'm not really sure how terminal size is defined, let alone how to write the cgo / syscall code to change the setting.

Are you able to assist in any way?

(I'm not sure if this helps explain the problem, but this query relates to my $SHELL project: https://github.com/lmorg/murex)

simulating tmux and screen functionality

Thanks for this library. I am trying to simulate tmux/screen/ssh style functionality.

I have a slightly modified version of the README example:

     func main() {
             c := exec.Command("/bin/bash")
             f, err := pty.Start(c)
             if err != nil {
                     panic(err)
             }
             go io.Copy(f, os.Stdin)
             //go io.Copy(os.Stderr, f)
             io.Copy(os.Stdout, f)
             log.Println("this test!")
     }

This seems to work except for when I use the arrow keys, or when I try to open vim.
When I use the arrow keys I see ^[[A , etc.
Is there something I am doing wrong?

disable echo, when using in go.crypto/ssh

Hello.
I'm try to use this package in golang ssh server to provide shell for user.
But i'm stuck at 2 points:

First: ssh client returns PTY allocation request failed on channel 0, when connect
Second: then i'm in ssh client enter command it echoed back with it output.

how can i resolve this issues?

Terminal size

  1. Getsize doesn't seem to work

    If I add the following to end of main() from this project's example

    rows, cols, err := pty.Getsize(f)
    if err == nil {
        println(rows, cols)
    }

    I get:

    $ go run pty.go
    foo
    bar
    baz
    bar
    0 0
    

    Am I doing something wrong?

  2. Is it possible to set the terminal size?

Installation instructions don't work

I have not been able to install on my ubuntu machine.

Some errors:

  1. can't import "exec"
  2. os.Error undefined

After fixing these issues, by replace exec by os/exec, and or.Error by error, I got this error, at which point I stopped.

./run.go:34: cannot use c.Stdin (type io.Reader) as type io.Writer in assignment:
io.Reader does not implement io.Writer (missing Write method)
./run.go:38: c.Stdin.Close undefined (type io.Reader has no field or method Close)
./run.go:73: cannot use f.Fd() (type uintptr) as type int in function argument
./run.go:83: cannot use f.Fd() (type uintptr) as type int in function argument
./run.go:95: undefined: os.ENOTTY

pty issue

In the file ioctl.go i'm getting "Multiple markers at this line
- undefined: syscall.SYS_IOCTL
- not enough arguments in call to
syscall.Syscall" for the line
"_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr)"
Could you please help me to resolve this issue.

Unexpected behaviour when used with GDB --tty on Mac OS X 10.10.3

First of all I want to apologise if the following is not an issue with this project, but I really don't know where to start.

Issue

As the title says, I tried to use the terminals returned by pty.Open() to interact with the target program with GDB. I try to provide a minimal working example that reproduce this issue.

Here's a dummy C program (hello.c):

#include <stdio.h>

int main()
{
    printf("Hello!\n");
    return 0;
}

Here's the Go program (issue.go):

package main

import (
    "github.com/kr/pty"
    "io"
    "os"
    "os/exec"
)

func main() {
    ptm, pts, err := pty.Open()
    if err != nil {
        panic(err)
    }
    println(ptm.Name())
    println(pts.Name())

    ttyName := pts.Name()
    // ttyName := "/dev/ttys000" // taken from $ tty

    gdb := exec.Command("gdb", "--tty", ttyName)

    stdin, err := gdb.StdinPipe()
    if err != nil {
        panic(err)
    }

    if err := gdb.Start(); err != nil {
        panic(err)
    }

    go func() {
        _, err := io.Copy(os.Stdout, ptm)
        if err != nil {
            panic(err)
        }
    }()

    if _, err := stdin.Write([]byte("file ./a.out\n")); err != nil {
        panic(err)
    }

    if _, err := stdin.Write([]byte("run\n")); err != nil {
        panic(err)
    }

    // this is not received by GDB when ttyName := pts.Name()
    if _, err := stdin.Write([]byte("run\n")); err != nil {
        panic(err)
    }

    if _, err := stdin.Write([]byte("quit\n")); err != nil {
        panic(err)
    }

    if err := gdb.Wait(); err != nil {
        panic(err)
    }
}

Now with:

gcc hello.c
go run issue.go

what I get is:

/dev/ptmx
/dev/ttys001
Hello!

then the program is stuck. While if I use a TTY of another terminal as ttyName in issue.go everything seems to work as expected:

$ tty
/dev/ttys000
$ tail -f /dev/null
warning: GDB: Failed to set controlling terminal: Operation not permitted
Hello!
warning: GDB: Failed to set controlling terminal: Operation not permitted
Hello!

And the program exits successfully.

Also, commenting out the second stdin.Write([]byte("run\n")) the program works even with a TTY coming from pty.Open().

System details

$ uname -a
Darwin pacinotti-wifi-219-203.unipi.it 14.3.0 Darwin Kernel Version 14.3.0: Mon Mar 23 11:59:05 PDT 2015; root:xnu-2782.20.48~5/RELEASE_X86_64 x86_64
$ go version
go version go1.4.2 darwin/amd64
$ gdb --version
GNU gdb (GDB) 7.9

If more are needed please do ask.

Cannot get DSR - Device Status Report

With regular ttys (os.Stdin, os.Stdout) I am able to send the DSR ansi escape sequence which will write back the cursor position as another ansi escape sequence. I would like to do the same with kr/pty but it seems to block indefinitely. Here is the reproduction:

package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"regexp"
	"strconv"

	"github.com/kr/pty"
	"golang.org/x/crypto/ssh/terminal"
)

var (
	dsrPattern = regexp.MustCompile(`(\d+);(\d+)`)
)

func main() {
	_, tty, err := pty.Open()
	if err != nil {
		log.Fatal(err)
	}

	if err := run(tty, tty); err != nil {
		log.Fatal(err)
	}

	// Compare with working version with os.Stdin, os.Stdout.
	//
	// if err := run(os.Stdin, os.Stdout); err != nil {
	// 	log.Fatal(err)
	// }
}

func run(in, out *os.File) error {
	oldState, err := terminal.MakeRaw(int(out.Fd()))
	if err != nil {
		return err
	}
	defer terminal.Restore(int(out.Fd()), oldState)

	// ANSI escape sequence for DSR - Device Status Report
	// https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences
	fmt.Fprint(out, "\x1b[6n")

	// Reports the cursor position (CPR) to the application as (as though typed at
	// the keyboard) ESC[n;mR, where n is the row and m is the column.
	reader := bufio.NewReader(in)
	text, err := reader.ReadSlice('R')
	if err != nil {
		return err
	}

	matches := dsrPattern.FindStringSubmatch(string(text))
	if len(matches) != 3 {
		return fmt.Errorf("incorrect number of matches: %d", len(matches))
	}

	col, err := strconv.Atoi(matches[2])
	if err != nil {
		return err
	}

	row, err := strconv.Atoi(matches[1])
	if err != nil {
		return err
	}

	fmt.Printf("col: %d, row: %d\n", col, row)
	return nil
}

My system:

~
❯ go version
go version go1.10.2 linux/amd64
~
❯ cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic
~
❯ uname -a
Linux triforce 4.4.0-127-generic #153-Ubuntu SMP Sat May 19 10:58:46 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
~echo $TERM
xterm-256color

Tests and Demos

The project lacks tests and demos.

We need to put them to demonstrate the functionality of the code.
I'm not so sure about it myself.

io.Copy blocked after closing

After ptmx is closed, io.Copy will not read and return an error.

example:

package main

import (
	"os/exec"
	"io"
	"fmt"
	"time"
	"github.com/creack/pty"
)

type w struct{}

func main() {
	cmd := exec.Command("bash")
	ptmx, err := pty.Start(cmd)
	if err != nil {
		panic(err)
	}
	go func() {
		time.Sleep(1* time.Second)
		fmt.Println("timeout")
		ptmx.Close()
	}()
	fmt.Println(io.Copy(&w{}, ptmx))
	fmt.Println("stop")
}

func (*w) Write(p []byte) (int,error) {
	return len(p), nil
}

example2:

func main() {
	cmd := exec.Command("bash")
	ptmx, err := pty.Start(cmd)
	if err != nil {
		panic(err)
	}
	go func() {
		time.Sleep(1 * time.Second)
		fmt.Println("timeout")
		ptmx.Close()
	}()
	body := make([]byte, 2048)
	for {
		n, err := ptmx.Read(body)
		if err != nil {
			fmt.Println(err)
			break
		}
		fmt.Println(body[0:n], err)
	}
	fmt.Println("stop")
}

arm64 support

Can we get a ztypes_arm64.go?

Apparently those are auto-generated somehow so I won't be sending a pull-request for the change I've done locally here which is basically copying the amd64 one to arm64.

Race condition in the example code

Hello,

it seems io.Copy is not blocked when pty is not closed, therefore I see race condition in the code. When I execute the example several times, I often do not see all the output, sometimes it is cut right after the first line:

$ ./go
foo

The expected output is I believe:

$ ./go
foo
bar
baz
bar

Fedora 17 64bits running Google Go 1.0.3.

When I put a sleep before the close command in the goroutine, it works fine. I am starting with go, I am not sure why Copy does not block in this case. When I tried to log error of the Copy func, I get:

read /dev/ptmx: bad file descriptor

I would expect it would block here, is this a bug?

pty does not cause change in controlling TTY (CTTY)

This issue means that programs like less (and others which expect to be able to use stdin separate from the terminal) will not work as expected if executed with pty.Start() or by changing their stdin/stdout.

The only way to fix this that I know of is to call fork, then call login_tty, or alternatively forkpty.

Output (expected child TTY to be equal to the value of stdin, but this is not the case)

$ go build && ./whatismytty -usepty
[parent] TTY: /dev/pts/5 stdin: /dev/pts/5
Starting with PTY
[ child] TTY: /dev/pts/5 stdin: /dev/pts/11
Child finished

Example program to show the issue is below.

package main

import (
    "bytes"
    "flag"
    "fmt"
    "log"
    "os"
    "os/exec"
    "strings"

    "github.com/kr/pty"
)

var child = flag.Bool("child", false, "Running as child")
var usepty = flag.Bool("usepty", false, "Use PTY with child")

func gettty() string {
    pid := fmt.Sprintf("%d", os.Getpid())
    c := exec.Command("ps", "--no-headers", "-p", pid, "eo", "tty")
    buf := &bytes.Buffer{}
    c.Stdout = buf
    c.Run()
    return "/dev/" + strings.TrimSpace(buf.String())
}

func getstdin() string {
    stdin, _ := os.Readlink("/proc/self/fd/0")
    return stdin
}

func main() {
    flag.Parse()

    proc := "parent"
    if *child {
        proc = "child"
    }

    tty := gettty()
    stdin := getstdin()

    log.Printf("[%6s] TTY: %s stdin: %s", proc, tty, stdin)

    if !*child {
        c := exec.Command(os.Args[0], "-child")
        if *usepty {
            log.Print("Starting with PTY")

            _, tty, err := pty.Open()
            if err != nil {
                panic(err)
            }
            defer tty.Close()

            c.Stdout = tty
            c.Stdin = tty
            // We need stderr to see child log output
            c.Stderr = os.Stdout
        } else {
            c.Stdin, c.Stdout, c.Stderr = os.Stdin, os.Stdout, os.Stderr
        }
        c.Run()
        log.Print("Child finished")
    }
}

Setpgid, PTY & “operation not permitted”

Here's a code snippet of running a bash script in a PTY:

pty-test.go

package main

import (
    "bytes"
    "fmt"
    "io"
    "os"
    "os/exec"
    "path"
    "sync"
    "syscall"

    "github.com/kr/pty"
)

func main() {
    wd, _ := os.Getwd()
    c := exec.Command(path.Join(wd, "pty-test.sh"))
    c.Dir = wd
    c.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}

    f, err := pty.Start(c)
    if err != nil {
        panic(err)
    }

    var wg sync.WaitGroup
    wg.Add(1)

    var buffer bytes.Buffer

    go func() {
        _, err = io.Copy(&buffer, f)
        if e, ok := err.(*os.PathError); ok && e.Err == syscall.EIO {
            // We can safely ignore this error, because it's just
            // the PTY telling us that it closed successfully. See:
            // https://github.com/buildkite/agent/pull/34#issuecomment-46080419
            err = nil
        }

        wg.Done()
    }()

    c.Wait()
    wg.Wait()

    fmt.Printf(buffer.String())
}

pty-test.sh

#!/bin/bash

echo "Oh hai there!"

If you build this program on OSX:

GOOS=linux GOARCH=amd64 go build -o pty-test pty-test.go

Then run it on Ubuntu, you get:

$ ./pty-test
Oh hai there!

If you build it directly on Ubuntu, or run it there via go run, you get:

$ go run pty-test.go
panic: fork/exec /root/pty-test.sh: operation not permitted

goroutine 1 [running]:
main.main()
    /root/pty-test.go:24 +0x229
exit status 2

If you remove the c.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} line, it all works as expected...

Can anyone give me any pointers of what I may be doing wrong?

Thanks!

Build for MIPS32

Hi!
Build golang 1.8 os=linux arch=mips error:

github.com/kr/pty
../github.com/kr/pty/pty_linux.go:34: undefined: _C_uint
../github.com/kr/pty/pty_linux.go:43: undefined: _C_int

inappropriate ioctl for device

Hi I take the shell example and change little and I'm getting this error, I think the error is that I'm running the code of the example in a go routine.

func InitTerminal(socket Sockets.SocketReadWriter) error {
 // Create arbitrary command.
 c := exec.Command("bash","-l")
 // 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.
        //  This is where it fails!!! *************
 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, socket) }()
 _, _ = io.Copy(socket, ptmx)

 return nil
}

And this is how I call the function

go func() {
	err := Terminal.InitTerminal(srw);
	if (err != nil){
		panic(err.Error() + ": panic initializing the terminal")
	}
}()

io.Copy(os.Stdout, f)

It does not work without io.Copy(os.Stdout, f)
What if I want result sent to a file?

Input/Output separation

Hello,
Thank you very much for the library.
I'm trying to find a way to separate input and output in a case like this:

func main() {

    cmd := exec.Command("sh", "-c", "mongo --quiet --host=localhost test") // user input, could be any shell command
    f, _ := pty.Start(cmd)
    cmd.Start()

    time.Sleep(1 * time.Second)

    for ;; {

        io.WriteString(f, "sleep(1000);Date.now();\n")
        stdoutScanner := bufio.NewScanner(f)

        go func() {
            for stdoutScanner.Scan() {
                text := stdoutScanner.Text()
                println(text)
            }
        }()

        time.Sleep(2000 * time.Millisecond)
    }
}

This code produces the following output:

> sleep(1000);Date.now();sleep(1000);Date.now();
1559185677551
sleep(1000);Date.now();
1559185679053
1559185691069
now();
1559185692573
);Date.now();sleep(1000);Date.now();
1559185694074
ep(1000);Date.now();sleep(1000);Date.now();

What I need is just

1559185677551
1559185679053
1559185691069
1559185692573
1559185694074

I can filter out all scanned text which contains user input, but that does not work stable - in the example you can find pieces like ep(1000);Date.now();

I would really appreciate if you can suggest a better solution

A little confuse about pty.StartWithSize

image
the high light code defer tty.Close()
I wonder why close slave after c.Start() without waiting it finishs
I have tried to close slave after cmd finished(the cmd is /bin/zsh), the origin shell can't come back
image

could you tell me the reason?

missing license file

Hi,

Which license is pty released under? GPL? MIT? Public domain? Copyrighted?

Best regards,
Alexander Rødseth

Reading from PTY in OSX makes the CPU go to 100%

The following go code running on OSX (version 10.12.6, MacBook Pro (Retina, 15-inch, Mid 2014)) will cause the CPU to go to 100% within 10 seconds, and stay there. It is due to the io.Copy(stdout, t) line. It doesn't happen on Ubuntu 16.04 (nor Android 5.1.1).

Anyone have any ideas as to why?

package main

import (
	"io"
	"log"
	"os"
	"os/exec"

	"golang.org/x/crypto/ssh/terminal"

	"github.com/kr/pty"
)

func main() {
	stdin := os.Stdin
	stdout := os.Stdout

	os.Stdin = nil
	os.Stdout = nil
	// os.Stderr = nil

	bash := exec.Command("/bin/bash")

	// Allocate a terminal for this channel
	t, err := pty.Start(bash)
	if err != nil {
		log.Fatalf("Could not start pty (%s)\n", err)
	}

	_, err = terminal.MakeRaw(int(stdin.Fd()))
	if err != nil {
		log.Fatalln(err)
	}

	go func() {
		if _, err := io.Copy(stdout, t); err != nil {
			log.Fatalln(err)
		}
	}()

	if _, err := io.Copy(t, stdin); err != nil {
		log.Fatalln(err)
	}
}

Windows subsystem as normal user errors on all commands.

Currently i'm trying to solve this issue: jesseduffield/lazygit#360

The short version of this issue is that when we use this package to run Git commands it errors on the Windows linux based subsystem as normal user with this error:
fork/exec /usr/bin/git: invalid arguments

Here is the code we use to do a git pull:

	cmd := exec.Command("git", "pull")

	cmd.Env = os.Environ()
	cmd.Env = append(cmd.Env, "LANG=en_US.UTF-8", "LC_ALL=en_US.UTF-8")

	var stderr bytes.Buffer
	cmd.Stderr = &stderr

	ptmx, err := pty.Start(cmd)

	if err != nil {
		return err // IT ERRORS HERE
	}

	// Other code

Do you know what the problem is here?
From other Github issues about the windows subsystem I've noticed that this might be related to the windows subsystem kernel.
Although there is one wired thing.. If the binary gets ran as root it works completely fine.

Do you have any idea why this happens or what might cause this.

Here are some other details:

  • OS: Windows subsystem Debian based
  • When running the commands with bash -c "git pull" also gives the same error (sh does the same thing)
  • When running as root it works fine
  • git fetch, pull, push all work perfectly fine when ran in the normal shell and without this package.
  • We use a fork of your package that is up to date with yours but with a different way of error reporting, here are the changes: jesseduffield@02db52c
  • This happens on all subsystems: Debian, OpenSuse, Ubuntu, etc

utils.go functions to not require os.File

Would you be open to modify the signature of the functions in utils.go:

  • InheritSize
  • SetSize
  • GetSizeFull
  • GetSize

to either accept a File-like interface:

type FileLike interface {
	Fd() uintptr
}

or to just require the the File descriptor myFile.Fd() instead of the os.File concrete struct?

func InheritSize(ptyFd, ttyFd uintptr) error {
...
}

If I am not mistaken otherwise it requires the caller to unnecessarily create a os.File object:

var myFileLike FileLike
myFile := os.NewFile(myFileLike.Fd(), "")
pty.InheritSize(myFile, myOtherFile)

Please tag a newer release

I noticed we've got a really old "release.r56" tag. It'd be really helpful if we could get a newer tag with all the improvements that have been made in the last three years. :)

NetBSD support

  I tried GOOS=netbsd GOARCH=amd64 ./mktypes.bash

  Added an "echo Complete" to the end to make sure the script ran fully, it
  ran. I didn't get a new _ztypes_netbsd_amd64.go

  How do I generate the netbsd ztype file?

  thanks!

i got error: EOF

server.js

code

'use strict'

let app = require('http').createServer(handler)
let io = require('socket.io')(app)

app.listen(3000)

console.log('Listening at http://localhost:3000/')

function handler (req, res) {
  res.writeHead(200)
  res.end('Testing server for http://github.com/wedeploy/gosocketio example.')
}
io.on('connection', function (socket) {
  console.log('Connecting %s.', socket.id)
  console.log(socket.request.headers)

  socket.on('messgae', (location) => {
    // fail booking 50% of the requests
    console.log('client message!')
    console.log(location)



    socket.send("l")
    socket.send("s")
    socket.send("\n")
  })

  socket.on('result', (location) => {
    // fail booking 50% of the requests
    console.log('result message!')
    console.log(location)

  })


  socket.on('find_tickets', (route) => {
    console.log('Quote for tickets from %s to %s requested.', route.From, route.To)
  })

  socket.on('error', (err) => {
    console.error(err)
  })

  socket.on('disconnect', () => {
    console.log('Disconnecting %s.', socket.id)
  })
})

client.go

code

package main

import (
	"bytes"
	"encoding/gob"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"time"

	"github.com/Ali-IoT-Lab/socketio-client-go"
	"github.com/kr/pty"
)

type wsPty struct {
	Cmd *exec.Cmd // pty builds on os.exec
	Pty *os.File  // a pty is simply an os.File
}

func (wp *wsPty) Start() {
	var err error
	args := flag.Args()
	wp.Cmd = exec.Command(cmdFlag, args...)
	wp.Cmd.Env = append(os.Environ(), "TERM=xterm")
	wp.Pty, err = pty.Start(wp.Cmd)
	if err != nil {
		log.Fatalf("Failed to start command: %s\n", err)
	}
}

func (wp *wsPty) Stop() {
	wp.Pty.Close()
	wp.Cmd.Wait()
}

func GetBytes(key interface{}) ([]byte, error) {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(key)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil

}

var cmdFlag string

func init() {
	flag.StringVar(&cmdFlag, "cmd", "/bin/bash", "command to execute on slave side of the pty")
	// TODO: make sure paths exist and have correct permissions
}
func main() {

	wp := wsPty{}
	// TODO: check for errors, return 500 on fail
	wp.Start()

	var Header http.Header = map[string][]string{
		"moja":     {"ccccc, asdasdasdasd"},
		"terminal": {"en-esadasdasdwrw"},
		"success":  {"dasdadas", "wdsadaderew"},
	}

	s, err := socketio.Socket("ws://127.0.0.1:3000")
	if err != nil {
		panic(err)
	}
	s.Connect(Header)
	s.Emit("messgae", "hello server!")

	s.On("message", func(args ...interface{}) {
		fmt.Println("-------------------11111-------------------------")
		//fmt.Printf("%T\n", args[0])
		res, _ := GetBytes(args[0])
		fmt.Println(string(res))
		wp.Pty.Write(res)
	})

	go func() {
		resBuf := make([]byte, 1024)
		for {
			fmt.Println("-----------------------2222222---------------------------")
			fmt.Println(string(resBuf))

			n, err := wp.Pty.Read(resBuf)
			if err != nil {
				log.Printf("Failed to read from pty master: %s", err)
				return
			}
			fmt.Println(n)
			// 	out := make([]byte, base64.StdEncoding.EncodedLen(n))
			// 	base64.StdEncoding.Encode(out, resBuf[0:n])

			// 	fmt.Println("-----------------------3333333---------------------------")
			// 	fmt.Println(string(resBuf[0:n]))

			// 	s.Emit("result", string(resBuf[0:n]))
		}

	}()

	time.Sleep(2 * time.Second)

	//wp.Stop()
	for {
		// s.Emit("messgae", "hello server!")
		// time.Sleep(2 * time.Second)
	}
}

why EOF ? l can't solve it ? Can you help me modify my code?

Trying to redirect socket I/O to the terminal

Hi I'm trying to redirect the input and output of sockets into the input and output of the terminal and can't make it work. The problem has to do with Stdin and stuff that I don't understand (file descriptor of files that are not really files etc).

Any help, I can provider all the code you need.

I'm using the following socket library:
socket io in go

This is my code:

main.go

package main

import (
 "github.com/googollee/go-socket.io"
 "log"
 "net/http"
 "rgui/Sockets"
 "rgui/Terminal"
)

const (
 port = ":2000"
)

type CorseMiddleware struct {
}

func (CorseMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {

 //Allow CORS here By * or specific origin
 w.Header().Set("Access-Control-Allow-Origin", r.Header["Origin"][0])
 w.Header().Set("Access-Control-Allow-Headers", "x-requested-with, Content-Type, origin, authorization, accept")
 w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT, PATCH")
 w.Header().Set("Access-Control-Allow-Credentials", "true")
 // return "OKOK"
 server.ServeHTTP(w, r)

}

var server *socketio.Server
var middleWare CorseMiddleware

func setUpSocket(so socketio.Socket) {
 srw := Sockets.CreateSocketReadWriter(&so, "data", "data");
 err := Terminal.InitTerminal(srw);
 if (err != nil) {
  panic(err.Error() + ": panic initializing the terminal")
 }
}

func main() {
 var err error
 server, err = socketio.NewServer(nil)
 if err != nil {
  log.Fatal(err)
 }

 _ = server.On("connection", func(so socketio.Socket) {
  setUpSocket(so);
 })
 _ = server.On("error", func(so socketio.Socket, err error) {
  log.Println("error:", err)
 })

 http.Handle("/socket.io/", middleWare)
 http.Handle("/", http.FileServer(http.Dir("./asset")))
 log.Println("Serving at localhost:5000...")
 log.Fatal(http.ListenAndServe(port, nil))
}

terminal.go

package Terminal

import (
 "github.com/kr/pty"
 "io"
 "log"
 "os"
 "os/exec"
 "os/signal"
 "rgui/Sockets"
 "syscall"
)


func InitTerminal(socket Sockets.SocketReadWriter) error {
 // Create arbitrary command.
 c := exec.Command("bash","-l")

 // 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())) // problems with this ***********
 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, socket) }()
 go func() { _, _ = io.Copy(socket, ptmx) }()

 return nil
}

Sockets.go

package Sockets

import (
 "fmt"
 "github.com/googollee/go-socket.io"
)

type SocketReadWriter struct {
 Socket *socketio.Socket
 ReadChannel string
 WriteChannel string
 ch  *(chan []byte)
}

func CreateSocketReadWriter(Socket *socketio.Socket, ReadChannel string, WriteChannel string) (srw SocketReadWriter){
 srw = SocketReadWriter{
  Socket :Socket,
  ReadChannel:ReadChannel,
  WriteChannel:WriteChannel,
  ch : new(chan []byte),
 }

 _ = (*srw.Socket).On(srw.ReadChannel, func(msg string) {
  fmt.Println(msg)
  (*srw.ch) <- []byte(msg)
 })
 return
}

func (srw SocketReadWriter) Write(p []byte) (n int, err error){
 err = (*srw.Socket).Emit(srw.WriteChannel, p)
 return len(p), err
}

func (srw SocketReadWriter) Read(p []byte) (n int, err error){
 p = <- (*srw.ch)
 return len(p), err
}

I don't understand those func .

func ptsname(f *os.File) (string, error)
-- ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n)))
func unlockpt(f *os.File) error
Why is there no comment...

i got error at log.Printf("Failed to read from pty master: %s", err) "forum/#!forum/golang-nuts" Nobody helps me. I know you can.

server.js

code

'use strict'

let app = require('http').createServer(handler)
let io = require('socket.io')(app)

app.listen(3000)

console.log('Listening at http://localhost:3000/')

function handler (req, res) {
  res.writeHead(200)
  res.end('Testing server for http://github.com/wedeploy/gosocketio example.')
}
io.on('connection', function (socket) {
  console.log('Connecting %s.', socket.id)
  console.log(socket.request.headers)

  socket.on('messgae', (location) => {
    // fail booking 50% of the requests
    console.log('client message!')
    console.log(location)


    socket.send("t")
    socket.send("o")
    socket.send("p")
    socket.send("\n")
  })

  socket.on('result', (location) => {
    // fail booking 50% of the requests
    console.log('result message!')
    console.log(location)

  })


  socket.on('find_tickets', (route) => {
    console.log('Quote for tickets from %s to %s requested.', route.From, route.To)
  })

  socket.on('error', (err) => {
    console.error(err)
  })

  socket.on('disconnect', () => {
    console.log('Disconnecting %s.', socket.id)
  })
})

client.go

code

package main

import (
	"bytes"
	"encoding/gob"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"time"

	"github.com/Ali-IoT-Lab/socketio-client-go"
	"github.com/kr/pty"
)

type wsPty struct {
	Cmd *exec.Cmd // pty builds on os.exec
	Pty *os.File  // a pty is simply an os.File
}

func (wp *wsPty) Start() {
	var err error
	args := flag.Args()
	wp.Cmd = exec.Command(cmdFlag, args...)
	wp.Cmd.Env = append(os.Environ(), "TERM=xterm")
	wp.Pty, err = pty.Start(wp.Cmd)
	if err != nil {
		log.Fatalf("Failed to start command: %s\n", err)
	}
}

func (wp *wsPty) Stop() {
	wp.Pty.Close()
	wp.Cmd.Wait()
}

func GetBytes(key interface{}) ([]byte, error) {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(key)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil

}

var cmdFlag string

func init() {
	flag.StringVar(&cmdFlag, "cmd", "/bin/bash", "command to execute on slave side of the pty")
	// TODO: make sure paths exist and have correct permissions
}
func main() {

	wp := wsPty{}
	// TODO: check for errors, return 500 on fail
	wp.Start()

	var Header http.Header = map[string][]string{
		"moja":     {"ccccc, asdasdasdasd"},
		"terminal": {"en-esadasdasdwrw"},
		"success":  {"dasdadas", "wdsadaderew"},
	}

	s, err := socketio.Socket("ws://127.0.0.1:3000")
	if err != nil {
		panic(err)
	}
	s.Connect(Header)
	s.Emit("messgae", "hello server!")

	s.On("message", func(args ...interface{}) {
		fmt.Println("-------------------11111-------------------------")
		//fmt.Printf("%T\n", args[0])
		res, _ := GetBytes(args[0])
		fmt.Println(string(res))
		wp.Pty.Write(res)
	})

	go func() {
		resBuf := make([]byte, 1024)
		for {
			fmt.Println("-----------------------2222222---------------------------")
			fmt.Println(string(resBuf))

			n, err := wp.Pty.Read(resBuf)
			if err != nil {
				log.Printf("Failed to read from pty master: %s", err)
				return
			}
			fmt.Println(n)
			// 	out := make([]byte, base64.StdEncoding.EncodedLen(n))
			// 	base64.StdEncoding.Encode(out, resBuf[0:n])

			// 	fmt.Println("-----------------------3333333---------------------------")
			// 	fmt.Println(string(resBuf[0:n]))

			// 	s.Emit("result", string(resBuf[0:n]))
		}

	}()

	time.Sleep(2 * time.Second)

	//wp.Stop()
	for {
		// s.Emit("messgae", "hello server!")
		// time.Sleep(2 * time.Second)
	}
}

How to put execution results in a variable? like top?

14_06_16__03_29_2019

this is not what i want ,i did below:

10_32_23__03_29_2019

but ,'ls' and '\n' is not what i want. if i write 'top', i need put results in a variable real time。

it lookes like a event which can monitor execution results。

can you help me?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.