func testCodepage(t *testing.T, name string, inReader, outReader func(io.Reader) io.Reader) {
	data := make([]byte, 256)
	for i := range data {
		data[i] = byte(i)
	}
	inr := inReader(bytes.NewBuffer(data))
	r, err := charset.NewReader(name, inr)
	if err != nil {
		t.Fatalf("cannot make reader for charset %q: %v", name, err)
	}
	outr := outReader(r)
	r = outr

	var outbuf bytes.Buffer
	w, err := charset.NewWriter(name, &outbuf)
	if err != nil {
		t.Fatalf("cannot make writer  for charset %q: %v", name, err)
	}
	_, err = io.Copy(w, r)
	if err != nil {
		t.Fatalf("copy failed: %v", err)
	}
	err = w.Close()
	if err != nil {
		t.Fatalf("close failed: %v", err)
	}
	if len(outbuf.Bytes()) != len(data) {
		t.Fatalf("short result of roundtrip, charset %q, readers %T, %T; expected 256, got %d", name, inr, outr, len(outbuf.Bytes()))
	}
	for i, x := range outbuf.Bytes() {
		if data[i] != x {
			t.Fatalf("charset %q, round trip expected %d, got %d", name, i, data[i])
		}
	}
}
func ExampleNewWriter() {
	buf := new(bytes.Buffer)
	w, err := charset.NewWriter("latin1", buf)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Fprintf(w, "£5 for Peppé")
	w.Close()
	fmt.Printf("%q\n", buf.Bytes())
	// Output: "\xa35 for Pepp\xe9"
}
Exemple #3
0
// NewPTY() is a newer version base on pty package, this opens from /dev/ptmx
// and find the slave tty from the /dev/pts folder automatically
func NewPTY() (*PTY, error) {
	pty, tty, err := pty.Open()
	if err != nil {
		return nil, err
	}

	masterEncoded, err := charset.NewWriter("ISO-8859-1", pty)
	if err != nil {
		return nil, err
	}

	return &PTY{
		Master:        pty,
		Slave:         tty,
		No:            0,
		MasterEncoded: masterEncoded,
	}, nil
}
func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "usage: tcs [-l] [-v] [charset]\n")
		fmt.Fprintf(os.Stderr, "\ttcs [-f charset] [-t charset] [file]\n")
	}
	flag.Parse()
	if *listFlag {
		cs := ""
		switch flag.NArg() {
		case 1:
			cs = flag.Arg(0)
		case 0:
		default:
			flag.Usage()
		}
		listCharsets(*verboseFlag, cs)
		return
	}
	var f *os.File
	switch flag.NArg() {
	case 0:
		f = os.Stdin
	case 1:
		var err error
		f, err = os.Open(flag.Arg(0))
		if err != nil {
			fatalf("cannot open %q: %v", err)
		}
	}
	r, err := charset.NewReader(*fromCharset, f)
	if err != nil {
		fatalf("cannot translate from %q: %v", *fromCharset, err)
	}
	w, err := charset.NewWriter(*toCharset, os.Stdout)
	if err != nil {
		fatalf("cannot translate to %q: ", err)
	}
	_, err = io.Copy(w, r)
	if err != nil {
		fatalf("%v", err)
	}
}
Exemple #5
0
func New(ptsPath string) (*PTY, error) {
	pty, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
	if err != nil {
		return nil, fmt.Errorf("open pty %s", err)
	}

	sname, err := ptsname(pty)
	if err != nil {
		return nil, fmt.Errorf("ptsname %s", err)
	}

	err = grantpt(pty)
	if err != nil {
		return nil, fmt.Errorf("grantpt %s", err)
	}

	err = unlockpt(pty)
	if err != nil {
		return nil, fmt.Errorf("unlockpt %s", err)
	}

	tty, err := os.OpenFile(sname, os.O_RDWR, 0)
	if err != nil {
		return nil, fmt.Errorf("open tty %s", err)
	}

	masterEncoded, err := charset.NewWriter("ISO-8859-1", pty)
	if err != nil {
		return nil, fmt.Errorf("charset %s", err)
	}

	return &PTY{
		Master:        pty,
		Slave:         tty,
		No:            0,
		MasterEncoded: masterEncoded,
	}, nil
}
Exemple #6
0
func New(ptsPath string) (*PTY, error) {
	// open master
	master, err := os.OpenFile(ptsPath+"/ptmx", os.O_RDWR, 0)
	if err != nil {
		return nil, fmt.Errorf("open pty %s", err)
	}

	pty := &PTY{Master: master}

	// unlock slave
	var unlock int32
	if err := pty.Ioctl(syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&unlock))); err != nil {
		return nil, fmt.Errorf("failed to unlock pty %s", err)
	}

	// find out slave name
	var ptyno uint32
	if err := pty.Ioctl(syscall.TIOCGPTN, uintptr(unsafe.Pointer(&ptyno))); err != nil {
		return nil, fmt.Errorf("failed to get ptyno %s", err)
	}
	pty.No = int(ptyno)

	// open slave
	slave, err := os.OpenFile(ptsPath+"/"+strconv.Itoa(pty.No), os.O_RDWR|syscall.O_NOCTTY, 0)
	if err != nil {
		return nil, fmt.Errorf("open tty %s", err)
	}
	pty.Slave = slave

	// apply proper encoding
	masterEncoded, err := charset.NewWriter("ISO-8859-1", master)
	if err != nil {
		return nil, fmt.Errorf("charset %s", err)
	}
	pty.MasterEncoded = masterEncoded

	return pty, nil
}
Exemple #7
0
// Connect connects to an existing Container by spawning a new process and
// attaching to it.
func (d *Docker) Connect(r *kite.Request) (interface{}, error) {
	var params struct {
		// The ID of the container. This needs to be created and started before
		// we can use Connect.
		ID string

		// Cmd contains the command which is executed and passed to the docker
		// exec api. If empty "bash" is used.
		Cmd string

		SizeX, SizeY int

		Remote Remote
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, err
	}

	if params.ID == "" {
		return nil, errors.New("missing arg: container ID is empty")
	}

	cmd := []string{"bash"}
	if params.Cmd != "" {
		cmd = strings.Fields(params.Cmd)
	}

	createOpts := dockerclient.CreateExecOptions{
		Container: params.ID,
		Tty:       true,
		Cmd:       cmd,
		// we attach to anything, it's used in the same was as with `docker
		// exec`
		AttachStdout: true,
		AttachStderr: true,
		AttachStdin:  true,
	}

	// now we create a new Exec instance. It will return us an exec ID which
	// will be used to start the created exec instance
	d.log.Info("Creating exec instance")
	ex, err := d.client.CreateExec(createOpts)
	if err != nil {
		return nil, err
	}

	// these pipes are important as we are acting as a proxy between the
	// Browser (client side) and Docker Deamon. For example, every input coming
	// from the client side is being written into inWritePipe and this input is
	// then read by docker exec via the inReadPipe.
	inReadPipe, inWritePipe := io.Pipe()
	outReadPipe, outWritePipe := io.Pipe()

	opts := dockerclient.StartExecOptions{
		Detach:       false,
		Tty:          true,
		OutputStream: outWritePipe,
		ErrorStream:  outWritePipe, // this is ok, that's how tty works
		InputStream:  inReadPipe,
	}

	// Control characters needs to be in ISO-8859 charset, so be sure that
	// UTF-8 writes are translated to this charset, for more info:
	// http://en.wikipedia.org/wiki/Control_character
	controlSequence, err := charset.NewWriter("ISO-8859-1", inWritePipe)
	if err != nil {
		return nil, err
	}

	errCh := make(chan error)
	closeCh := make(chan bool)

	server := &Server{
		Session:         ex.ID,
		remote:          params.Remote,
		out:             outReadPipe,
		in:              inWritePipe,
		controlSequence: controlSequence,
		closeChan:       closeCh,
		client:          d.client,
	}

	go func() {
		d.log.Info("Starting exec instance '%s'", ex.ID)
		err := d.client.StartExec(ex.ID, opts)
		errCh <- err

		// call the remote function that we ended the session
		server.remote.SessionEnded.Call()
	}()

	go func() {
		select {
		case err := <-errCh:
			if err != nil {
				d.log.Error("startExec error: ", err)
			}
		case <-closeCh:
			// once we close them the underlying hijack process in docker
			// client package will end too, which will close the underlying
			// connection once it's finished/returned.
			inReadPipe.CloseWithError(errors.New("user closed the session"))
			inWritePipe.CloseWithError(errors.New("user closed the session"))

			outReadPipe.CloseWithError(errors.New("user closed the session"))
			outWritePipe.CloseWithError(errors.New("user closed the session"))
		}
	}()

	var once sync.Once

	// Read the STDOUT from shell process and send to the connected client.
	// https://github.com/koding/koding/commit/50cbd3609af93334150f7951dae49a23f71078f6
	go func() {
		buf := make([]byte, (1<<12)-utf8.UTFMax, 1<<12)
		for {
			n, err := server.out.Read(buf)
			for n < cap(buf)-1 {
				r, _ := utf8.DecodeLastRune(buf[:n])
				if r != utf8.RuneError {
					break
				}
				server.out.Read(buf[n : n+1])
				n++
			}

			// we need to set it for the first time. Because "StartExec" is
			// called in a goroutine and it's blocking there is now way we know
			// when it's ready. Therefore we set the size only once when get an
			// output. After that the client side is setting the TTY size with
			// the Server.SetSize method, that is called everytime the client
			// side sends a size command to us.
			once.Do(func() {
				// Y is  height, X is width
				err = d.client.ResizeExecTTY(ex.ID, int(params.SizeY), int(params.SizeX))
				if err != nil {
					fmt.Println("error resizing", err)
				}
			})

			server.remote.Output.Call(string(filterInvalidUTF8(buf[:n])))
			if err != nil {
				break
			}
		}
		d.log.Debug("Breaking out of for loop")
	}()

	d.log.Debug("Returning server")
	return server, nil
}