TBR

v: renaming the v directory to go

Change-Id: I4fd9f6ee2895d8034c23b65927eb118980b3c17a
diff --git a/lib/forward.go b/lib/forward.go
new file mode 100644
index 0000000..5a7c21f
--- /dev/null
+++ b/lib/forward.go
@@ -0,0 +1,71 @@
+package lib
+
+import (
+	"fmt"
+	"io"
+	"net"
+)
+
+type sender interface {
+	Send([]uint8) error
+}
+type receiver interface {
+	Recv() ([]uint8, error)
+}
+
+// stream is the interface common to TunnelForwardStream and TunnelServiceForwardStream.
+type stream interface {
+	sender
+	receiver
+}
+
+// Forward forwards data read from net.Conn to a TunnelForwardStream or a TunnelServiceForwardStream.
+func Forward(conn net.Conn, stream stream) error {
+	defer conn.Close()
+	// Both conn2stream and stream2conn will write to the channel exactly
+	// once.
+	// Forward reads from the channel exactly once.
+	// A buffered channel is used to prevent the other write to the channel
+	// from blocking.
+	done := make(chan error, 1)
+	go conn2stream(conn, stream, done)
+	go stream2conn(stream, conn, done)
+	return <-done
+}
+
+func conn2stream(r io.Reader, s sender, done chan error) {
+	var buf [2048]byte
+	for {
+		n, err := r.Read(buf[:])
+		if err == io.EOF {
+			done <- nil
+			return
+		}
+		if err != nil {
+			done <- err
+			return
+		}
+		if err := s.Send(buf[:n]); err != nil {
+			done <- err
+			return
+		}
+	}
+}
+
+func stream2conn(r receiver, w io.Writer, done chan error) {
+	for {
+		buf, err := r.Recv()
+		if err == io.EOF {
+			done <- nil
+			return
+		}
+		if err != nil {
+			done <- err
+			return
+		}
+		if n, err := w.Write(buf); n != len(buf) || err != nil {
+			done <- fmt.Errorf("conn.Write returned (%d, %v) want (%d, nil)", n, err, len(buf))
+			return
+		}
+	}
+}
diff --git a/lib/terminal.go b/lib/terminal.go
new file mode 100644
index 0000000..1e33bb1
--- /dev/null
+++ b/lib/terminal.go
@@ -0,0 +1,93 @@
+package lib
+
+import (
+	"errors"
+	"os/exec"
+	"strings"
+	"syscall"
+	"unsafe"
+
+	"veyron2/vlog"
+)
+
+// Used with ioctl TIOCGWINSZ and TIOCSWINSZ.
+type Winsize struct {
+	Row    uint16
+	Col    uint16
+	Xpixel uint16
+	Ypixel uint16
+}
+
+// SetWindowSize sets the terminal's window size.
+func SetWindowSize(fd uintptr, ws Winsize) error {
+	vlog.Infof("Setting window size: %v", ws)
+	ret, _, _ := syscall.Syscall(
+		syscall.SYS_IOCTL,
+		fd,
+		uintptr(syscall.TIOCSWINSZ),
+		uintptr(unsafe.Pointer(&ws)))
+	if int(ret) == -1 {
+		return errors.New("ioctl(TIOCSWINSZ) failed")
+	}
+	return nil
+}
+
+// GetWindowSize gets the terminal's window size.
+func GetWindowSize() (*Winsize, error) {
+	ws := &Winsize{}
+	ret, _, _ := syscall.Syscall(
+		syscall.SYS_IOCTL,
+		uintptr(syscall.Stdin),
+		uintptr(syscall.TIOCGWINSZ),
+		uintptr(unsafe.Pointer(ws)))
+	if int(ret) == -1 {
+		return nil, errors.New("ioctl(TIOCGWINSZ) failed")
+	}
+	return ws, nil
+}
+
+func EnterRawTerminalMode() string {
+	var savedBytes []byte
+	var err error
+	if savedBytes, err = exec.Command("stty", "-F", "/dev/tty", "-g").Output(); err != nil {
+		vlog.Infof("Failed to save terminal settings: %q (%v)", savedBytes, err)
+	}
+	saved := strings.TrimSpace(string(savedBytes))
+
+	args := []string{
+		"-F", "/dev/tty",
+		// Don't buffer stdin. Read characters as they are typed.
+		"-icanon", "min", "1", "time", "0",
+		// Turn off local echo of input characters.
+		"-echo", "-echoe", "-echok", "-echonl",
+		// Disable interrupt, quit, and suspend special characters.
+		"-isig",
+		// Ignore characters with parity errors.
+		"ignpar",
+		// Disable translate newline to carriage return.
+		"-inlcr",
+		// Disable ignore carriage return.
+		"-igncr",
+		// Disable translate carriage return to newline.
+		"-icrnl",
+		// Disable flow control.
+		"-ixon", "-ixany", "-ixoff",
+		// Disable non-POSIX special characters.
+		"-iexten",
+	}
+	if out, err := exec.Command("stty", args...).CombinedOutput(); err != nil {
+		vlog.Infof("stty failed (%v) (%q)", err, out)
+	}
+
+	return string(saved)
+}
+
+func RestoreTerminalSettings(saved string) {
+	args := []string{
+		"-F", "/dev/tty",
+		saved,
+	}
+	if out, err := exec.Command("stty", args...).CombinedOutput(); err != nil {
+		vlog.Infof("stty failed (%v) (%q)", err, out)
+	}
+}
diff --git a/tunnel.idl b/tunnel.idl
new file mode 100644
index 0000000..88eb2bf
--- /dev/null
+++ b/tunnel.idl
@@ -0,0 +1,40 @@
+package tunnel
+
+import "veyron2/security"
+
+// Tunnel creates a network tunnel from the client to the server.
+
+type Tunnel interface {
+  // The Forward method is used for network forwarding. All the data sent over
+  // the byte stream is forwarded to the requested network address and all the
+  // data received from that network connection is sent back in the reply
+  // stream.
+  Forward(network, address string) stream<[]byte, []byte> error {security.AdminLabel}
+
+  // The Shell method is used to either run shell commands remotely, or to open
+  // an interactive shell. The data received over the byte stream is sent to the
+  // shell's stdin, and the data received from the shell's stdout and stderr is
+  // sent back in the reply stream. It returns the exit status of the shell
+  // command.
+  Shell(command string, shellOpts ShellOpts) stream<ClientShellPacket, ServerShellPacket> (int32, error) {security.AdminLabel}
+}
+
+type ShellOpts struct {
+	UsePty       bool      // Whether to open a pseudo-terminal
+	Environment  []string  // Environment variables to pass to the remote shell.
+	Rows, Cols   uint32    // Window size.
+}
+
+type ClientShellPacket struct {
+        // Bytes going to the shell's stdin.
+        Stdin       []byte
+        // A dynamic update of the window size. The default value of 0 means no-change.
+        Rows, Cols  uint32
+}
+
+type ServerShellPacket struct {
+        // Bytes coming from the shell's stdout.
+        Stdout      []byte
+        // Bytes coming from the shell's stderr.
+        Stderr      []byte
+}
diff --git a/tunnel.idl.go b/tunnel.idl.go
new file mode 100644
index 0000000..14c039c
--- /dev/null
+++ b/tunnel.idl.go
@@ -0,0 +1,423 @@
+// This file was auto-generated by the veyron idl tool.
+// Source: tunnel.idl
+
+package tunnel
+
+import (
+	"veyron2/security"
+
+	// The non-user imports are prefixed with "_gen_" to prevent collisions.
+	_gen_idl "veyron2/idl"
+	_gen_ipc "veyron2/ipc"
+	_gen_naming "veyron2/naming"
+	_gen_rt "veyron2/rt/r"
+	_gen_wiretype "veyron2/wiretype"
+)
+
+type ShellOpts struct {
+	UsePty      bool     // Whether to open a pseudo-terminal
+	Environment []string // Environment variables to pass to the remote shell.
+	Rows        uint32   // Window size.
+	Cols        uint32
+}
+type ClientShellPacket struct {
+	// Bytes going to the shell's stdin.
+	Stdin []byte
+	// A dynamic update of the window size. The default value of 0 means no-change.
+	Rows uint32
+	Cols uint32
+}
+type ServerShellPacket struct {
+	// Bytes coming from the shell's stdout.
+	Stdout []byte
+	// Bytes coming from the shell's stderr.
+	Stderr []byte
+}
+
+// Tunnel is the interface the client binds and uses.
+// Tunnel_InternalNoTagGetter is the interface without the TagGetter
+// and UnresolveStep methods (both framework-added, rathern than user-defined),
+// to enable embedding without method collisions.  Not to be used directly by
+// clients.
+type Tunnel_InternalNoTagGetter interface {
+
+	// The Forward method is used for network forwarding. All the data sent over
+	// the byte stream is forwarded to the requested network address and all the
+	// data received from that network connection is sent back in the reply
+	// stream.
+	Forward(network string, address string, opts ..._gen_ipc.ClientCallOpt) (reply TunnelForwardStream, err error)
+
+	// The Shell method is used to either run shell commands remotely, or to open
+	// an interactive shell. The data received over the byte stream is sent to the
+	// shell's stdin, and the data received from the shell's stdout and stderr is
+	// sent back in the reply stream. It returns the exit status of the shell
+	// command.
+	Shell(command string, shellOpts ShellOpts, opts ..._gen_ipc.ClientCallOpt) (reply TunnelShellStream, err error)
+}
+type Tunnel interface {
+	_gen_idl.TagGetter
+	// UnresolveStep returns the names for the remote service, rooted at the
+	// service's immediate namespace ancestor.
+	UnresolveStep(opts ..._gen_ipc.ClientCallOpt) ([]string, error)
+	Tunnel_InternalNoTagGetter
+}
+
+// TunnelService is the interface the server implements.
+type TunnelService interface {
+
+	// The Forward method is used for network forwarding. All the data sent over
+	// the byte stream is forwarded to the requested network address and all the
+	// data received from that network connection is sent back in the reply
+	// stream.
+	Forward(context _gen_ipc.Context, network string, address string, stream TunnelServiceForwardStream) (err error)
+
+	// The Shell method is used to either run shell commands remotely, or to open
+	// an interactive shell. The data received over the byte stream is sent to the
+	// shell's stdin, and the data received from the shell's stdout and stderr is
+	// sent back in the reply stream. It returns the exit status of the shell
+	// command.
+	Shell(context _gen_ipc.Context, command string, shellOpts ShellOpts, stream TunnelServiceShellStream) (reply int32, err error)
+}
+
+// TunnelForwardStream is the interface for streaming responses of the method
+// Forward in the service interface Tunnel.
+type TunnelForwardStream interface {
+
+	// Send places the item onto the output stream, blocking if there is no buffer
+	// space available.
+	Send(item []byte) error
+
+	// CloseSend indicates to the server that no more items will be sent; server
+	// Recv calls will receive io.EOF after all sent items.  Subsequent calls to
+	// Send on the client will fail.  This is an optional call - it's used by
+	// streaming clients that need the server to receive the io.EOF terminator.
+	CloseSend() error
+
+	// Recv returns the next item in the input stream, blocking until
+	// an item is available.  Returns io.EOF to indicate graceful end of input.
+	Recv() (item []byte, err error)
+
+	// Finish closes the stream and returns the positional return values for
+	// call.
+	Finish() (err error)
+
+	// Cancel cancels the RPC, notifying the server to stop processing.
+	Cancel()
+}
+
+// Implementation of the TunnelForwardStream interface that is not exported.
+type implTunnelForwardStream struct {
+	clientCall _gen_ipc.ClientCall
+}
+
+func (c *implTunnelForwardStream) Send(item []byte) error {
+	return c.clientCall.Send(item)
+}
+
+func (c *implTunnelForwardStream) CloseSend() error {
+	return c.clientCall.CloseSend()
+}
+
+func (c *implTunnelForwardStream) Recv() (item []byte, err error) {
+	err = c.clientCall.Recv(&item)
+	return
+}
+
+func (c *implTunnelForwardStream) Finish() (err error) {
+	if ierr := c.clientCall.Finish(&err); ierr != nil {
+		err = ierr
+	}
+	return
+}
+
+func (c *implTunnelForwardStream) Cancel() {
+	c.clientCall.Cancel()
+}
+
+// TunnelServiceForwardStream is the interface for streaming responses of the method
+// Forward in the service interface Tunnel.
+type TunnelServiceForwardStream interface {
+	// Send places the item onto the output stream, blocking if there is no buffer
+	// space available.
+	Send(item []byte) error
+
+	// Recv fills itemptr with the next item in the input stream, blocking until
+	// an item is available.  Returns io.EOF to indicate graceful end of input.
+	Recv() (item []byte, err error)
+}
+
+// Implementation of the TunnelServiceForwardStream interface that is not exported.
+type implTunnelServiceForwardStream struct {
+	serverCall _gen_ipc.ServerCall
+}
+
+func (s *implTunnelServiceForwardStream) Send(item []byte) error {
+	return s.serverCall.Send(item)
+}
+
+func (s *implTunnelServiceForwardStream) Recv() (item []byte, err error) {
+	err = s.serverCall.Recv(&item)
+	return
+}
+
+// TunnelShellStream is the interface for streaming responses of the method
+// Shell in the service interface Tunnel.
+type TunnelShellStream interface {
+
+	// Send places the item onto the output stream, blocking if there is no buffer
+	// space available.
+	Send(item ClientShellPacket) error
+
+	// CloseSend indicates to the server that no more items will be sent; server
+	// Recv calls will receive io.EOF after all sent items.  Subsequent calls to
+	// Send on the client will fail.  This is an optional call - it's used by
+	// streaming clients that need the server to receive the io.EOF terminator.
+	CloseSend() error
+
+	// Recv returns the next item in the input stream, blocking until
+	// an item is available.  Returns io.EOF to indicate graceful end of input.
+	Recv() (item ServerShellPacket, err error)
+
+	// Finish closes the stream and returns the positional return values for
+	// call.
+	Finish() (reply int32, err error)
+
+	// Cancel cancels the RPC, notifying the server to stop processing.
+	Cancel()
+}
+
+// Implementation of the TunnelShellStream interface that is not exported.
+type implTunnelShellStream struct {
+	clientCall _gen_ipc.ClientCall
+}
+
+func (c *implTunnelShellStream) Send(item ClientShellPacket) error {
+	return c.clientCall.Send(item)
+}
+
+func (c *implTunnelShellStream) CloseSend() error {
+	return c.clientCall.CloseSend()
+}
+
+func (c *implTunnelShellStream) Recv() (item ServerShellPacket, err error) {
+	err = c.clientCall.Recv(&item)
+	return
+}
+
+func (c *implTunnelShellStream) Finish() (reply int32, err error) {
+	if ierr := c.clientCall.Finish(&reply, &err); ierr != nil {
+		err = ierr
+	}
+	return
+}
+
+func (c *implTunnelShellStream) Cancel() {
+	c.clientCall.Cancel()
+}
+
+// TunnelServiceShellStream is the interface for streaming responses of the method
+// Shell in the service interface Tunnel.
+type TunnelServiceShellStream interface {
+	// Send places the item onto the output stream, blocking if there is no buffer
+	// space available.
+	Send(item ServerShellPacket) error
+
+	// Recv fills itemptr with the next item in the input stream, blocking until
+	// an item is available.  Returns io.EOF to indicate graceful end of input.
+	Recv() (item ClientShellPacket, err error)
+}
+
+// Implementation of the TunnelServiceShellStream interface that is not exported.
+type implTunnelServiceShellStream struct {
+	serverCall _gen_ipc.ServerCall
+}
+
+func (s *implTunnelServiceShellStream) Send(item ServerShellPacket) error {
+	return s.serverCall.Send(item)
+}
+
+func (s *implTunnelServiceShellStream) Recv() (item ClientShellPacket, err error) {
+	err = s.serverCall.Recv(&item)
+	return
+}
+
+// BindTunnel returns the client stub implementing the Tunnel
+// interface.
+//
+// If no _gen_ipc.Client is specified, the default _gen_ipc.Client in the
+// global Runtime is used.
+func BindTunnel(name string, opts ..._gen_ipc.BindOpt) (Tunnel, error) {
+	var client _gen_ipc.Client
+	switch len(opts) {
+	case 0:
+		client = _gen_rt.R().Client()
+	case 1:
+		switch o := opts[0].(type) {
+		case _gen_ipc.Runtime:
+			client = o.Client()
+		case _gen_ipc.Client:
+			client = o
+		default:
+			return nil, _gen_idl.ErrUnrecognizedOption
+		}
+	default:
+		return nil, _gen_idl.ErrTooManyOptionsToBind
+	}
+	stub := &clientStubTunnel{client: client, name: name}
+
+	return stub, nil
+}
+
+// NewServerTunnel creates a new server stub.
+//
+// It takes a regular server implementing the TunnelService
+// interface, and returns a new server stub.
+func NewServerTunnel(server TunnelService) interface{} {
+	return &ServerStubTunnel{
+		service: server,
+	}
+}
+
+// clientStubTunnel implements Tunnel.
+type clientStubTunnel struct {
+	client _gen_ipc.Client
+	name   string
+}
+
+func (c *clientStubTunnel) GetMethodTags(method string) []interface{} {
+	return GetTunnelMethodTags(method)
+}
+
+func (__gen_c *clientStubTunnel) Forward(network string, address string, opts ..._gen_ipc.ClientCallOpt) (reply TunnelForwardStream, err error) {
+	var call _gen_ipc.ClientCall
+	if call, err = __gen_c.client.StartCall(__gen_c.name, "Forward", []interface{}{network, address}, opts...); err != nil {
+		return
+	}
+	reply = &implTunnelForwardStream{clientCall: call}
+	return
+}
+
+func (__gen_c *clientStubTunnel) Shell(command string, shellOpts ShellOpts, opts ..._gen_ipc.ClientCallOpt) (reply TunnelShellStream, err error) {
+	var call _gen_ipc.ClientCall
+	if call, err = __gen_c.client.StartCall(__gen_c.name, "Shell", []interface{}{command, shellOpts}, opts...); err != nil {
+		return
+	}
+	reply = &implTunnelShellStream{clientCall: call}
+	return
+}
+
+func (c *clientStubTunnel) UnresolveStep(opts ..._gen_ipc.ClientCallOpt) (reply []string, err error) {
+	var call _gen_ipc.ClientCall
+	if call, err = c.client.StartCall(c.name, "UnresolveStep", nil, opts...); err != nil {
+		return
+	}
+	if ierr := call.Finish(&reply, &err); ierr != nil {
+		err = ierr
+	}
+	return
+}
+
+// ServerStubTunnel wraps a server that implements
+// TunnelService and provides an object that satisfies
+// the requirements of veyron2/ipc.ReflectInvoker.
+type ServerStubTunnel struct {
+	service TunnelService
+}
+
+func (s *ServerStubTunnel) GetMethodTags(method string) []interface{} {
+	return GetTunnelMethodTags(method)
+}
+
+func (s *ServerStubTunnel) Signature(call _gen_ipc.ServerCall) (_gen_ipc.ServiceSignature, error) {
+	result := _gen_ipc.ServiceSignature{Methods: make(map[string]_gen_ipc.MethodSignature)}
+	result.Methods["Forward"] = _gen_ipc.MethodSignature{
+		InArgs: []_gen_ipc.MethodArgument{
+			{Name: "network", Type: 3},
+			{Name: "address", Type: 3},
+		},
+		OutArgs: []_gen_ipc.MethodArgument{
+			{Name: "", Type: 65},
+		},
+		InStream:  67,
+		OutStream: 67,
+	}
+	result.Methods["Shell"] = _gen_ipc.MethodSignature{
+		InArgs: []_gen_ipc.MethodArgument{
+			{Name: "command", Type: 3},
+			{Name: "shellOpts", Type: 68},
+		},
+		OutArgs: []_gen_ipc.MethodArgument{
+			{Name: "", Type: 36},
+			{Name: "", Type: 65},
+		},
+		InStream:  69,
+		OutStream: 70,
+	}
+
+	result.TypeDefs = []_gen_idl.AnyData{
+		_gen_wiretype.NamedPrimitiveType{Type: 0x1, Name: "error", Tags: []string(nil)}, _gen_wiretype.NamedPrimitiveType{Type: 0x32, Name: "byte", Tags: []string(nil)}, _gen_wiretype.SliceType{Elem: 0x42, Name: "", Tags: []string(nil)}, _gen_wiretype.StructType{
+			[]_gen_wiretype.FieldType{
+				_gen_wiretype.FieldType{Type: 0x2, Name: "UsePty"},
+				_gen_wiretype.FieldType{Type: 0x3d, Name: "Environment"},
+				_gen_wiretype.FieldType{Type: 0x34, Name: "Rows"},
+				_gen_wiretype.FieldType{Type: 0x34, Name: "Cols"},
+			},
+			"ShellOpts", []string(nil)},
+		_gen_wiretype.StructType{
+			[]_gen_wiretype.FieldType{
+				_gen_wiretype.FieldType{Type: 0x43, Name: "Stdin"},
+				_gen_wiretype.FieldType{Type: 0x34, Name: "Rows"},
+				_gen_wiretype.FieldType{Type: 0x34, Name: "Cols"},
+			},
+			"ClientShellPacket", []string(nil)},
+		_gen_wiretype.StructType{
+			[]_gen_wiretype.FieldType{
+				_gen_wiretype.FieldType{Type: 0x43, Name: "Stdout"},
+				_gen_wiretype.FieldType{Type: 0x43, Name: "Stderr"},
+			},
+			"ServerShellPacket", []string(nil)},
+	}
+
+	return result, nil
+}
+
+func (s *ServerStubTunnel) UnresolveStep(call _gen_ipc.ServerCall) (reply []string, err error) {
+	if unresolver, ok := s.service.(_gen_ipc.Unresolver); ok {
+		return unresolver.UnresolveStep(call)
+	}
+	if call.Server() == nil {
+		return
+	}
+	var published []string
+	if published, err = call.Server().Published(); err != nil || published == nil {
+		return
+	}
+	reply = make([]string, len(published))
+	for i, p := range published {
+		reply[i] = _gen_naming.Join(p, call.Name())
+	}
+	return
+}
+
+func (__gen_s *ServerStubTunnel) Forward(call _gen_ipc.ServerCall, network string, address string) (err error) {
+	stream := &implTunnelServiceForwardStream{serverCall: call}
+	err = __gen_s.service.Forward(call, network, address, stream)
+	return
+}
+
+func (__gen_s *ServerStubTunnel) Shell(call _gen_ipc.ServerCall, command string, shellOpts ShellOpts) (reply int32, err error) {
+	stream := &implTunnelServiceShellStream{serverCall: call}
+	reply, err = __gen_s.service.Shell(call, command, shellOpts, stream)
+	return
+}
+
+func GetTunnelMethodTags(method string) []interface{} {
+	switch method {
+	case "Forward":
+		return []interface{}{security.Label(4)}
+	case "Shell":
+		return []interface{}{security.Label(4)}
+	default:
+		return nil
+	}
+}
diff --git a/tunneld/impl/impl.go b/tunneld/impl/impl.go
new file mode 100644
index 0000000..cd210ed
--- /dev/null
+++ b/tunneld/impl/impl.go
@@ -0,0 +1,162 @@
+package impl
+
+import (
+	"github.com/kr/pty"
+
+	"errors"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"net"
+	"os"
+	"os/exec"
+	"syscall"
+
+	"veyron/examples/tunnel"
+	"veyron/examples/tunnel/lib"
+	"veyron2/ipc"
+	"veyron2/vlog"
+)
+
+// T implements tunnel.TunnelService
+type T struct {
+}
+
+func (t *T) Forward(ctx ipc.Context, network, address string, stream tunnel.TunnelServiceForwardStream) error {
+	conn, err := net.Dial(network, address)
+	if err != nil {
+		return err
+	}
+	name := fmt.Sprintf("RemoteID:%v LocalAddr:%v RemoteAddr:%v", ctx.RemoteID(), conn.LocalAddr(), conn.RemoteAddr())
+	vlog.Infof("TUNNEL START: %v", name)
+	err = lib.Forward(conn, stream)
+	vlog.Infof("TUNNEL END  : %v (%v)", name, err)
+	return err
+}
+
+func (t *T) Shell(ctx ipc.Context, command string, shellOpts tunnel.ShellOpts, stream tunnel.TunnelServiceShellStream) (int32, error) {
+	vlog.Infof("SHELL START for %v: %q", ctx.RemoteID(), command)
+
+	const nonShellErrorCode = 255
+
+	shell, err := findShell()
+	if err != nil {
+		return nonShellErrorCode, err
+	}
+
+	var c *exec.Cmd
+	// An empty command means that we need an interactive shell.
+	if len(command) == 0 {
+		c = exec.Command(shell, "-i")
+		sendMotd(stream)
+	} else {
+		c = exec.Command(shell, "-c", command)
+	}
+
+	c.Env = []string{
+		fmt.Sprintf("HOME=%s", os.Getenv("HOME")),
+		fmt.Sprintf("VEYRON_LOCAL_IDENTITY=%s", ctx.LocalID()),
+		fmt.Sprintf("VEYRON_REMOTE_IDENTITY=%s", ctx.RemoteID()),
+	}
+	c.Env = append(c.Env, shellOpts.Environment...)
+	vlog.Infof("Shell environment: %v", c.Env)
+
+	c.Dir = os.Getenv("HOME")
+	vlog.Infof("Shell CWD: %v", c.Dir)
+
+	var (
+		stdin          io.WriteCloser // We write to stdin.
+		stdout, stderr io.ReadCloser  // We read from stdout and stderr.
+		ptyFd          uintptr        // File descriptor for pty.
+	)
+
+	if shellOpts.UsePty {
+		f, err := pty.Start(c)
+		if err != nil {
+			return nonShellErrorCode, err
+		}
+		stdin = f
+		stdout = f
+		stderr = nil
+		ptyFd = f.Fd()
+
+		defer f.Close()
+
+		setWindowSize(ptyFd, shellOpts.Rows, shellOpts.Cols)
+	} else {
+		var err error
+		if stdin, err = c.StdinPipe(); err != nil {
+			return nonShellErrorCode, err
+		}
+		defer stdin.Close()
+
+		if stdout, err = c.StdoutPipe(); err != nil {
+			return nonShellErrorCode, err
+		}
+		defer stdout.Close()
+
+		if stderr, err = c.StderrPipe(); err != nil {
+			return nonShellErrorCode, err
+		}
+		defer stderr.Close()
+
+		if err = c.Start(); err != nil {
+			vlog.Infof("Cmd.Start failed: %v", err)
+			return nonShellErrorCode, err
+		}
+	}
+
+	defer c.Process.Kill()
+
+	ferr := runIOManager(stdin, stdout, stderr, ptyFd, stream)
+	vlog.Infof("SHELL END for %v: %q (%v)", ctx.RemoteID(), command, ferr)
+
+	// Check the exit status.
+	var status syscall.WaitStatus
+	if _, err := syscall.Wait4(c.Process.Pid, &status, syscall.WNOHANG, nil); err != nil {
+		return nonShellErrorCode, err
+	}
+	if status.Signaled() {
+		return int32(status), fmt.Errorf("process killed by signal %d (%v)", int(status.Signal()), status.Signal())
+	}
+	if status.Exited() {
+		if status.ExitStatus() == 0 {
+			return 0, nil
+		}
+		return int32(status.ExitStatus()), fmt.Errorf("process exited with exit status %d", status.ExitStatus())
+	}
+
+	// The process has not exited. Use the error from ForwardStdIO.
+	return nonShellErrorCode, ferr
+}
+
+// findShell returns the path to the first usable shell binary.
+func findShell() (string, error) {
+	shells := []string{"/bin/bash", "/bin/sh"}
+	for _, s := range shells {
+		if _, err := os.Stat(s); err == nil {
+			return s, nil
+		}
+	}
+	return "", errors.New("could not find any shell binary")
+}
+
+// sendMotd sends the content of the MOTD file to the stream, if it exists.
+func sendMotd(s tunnel.TunnelServiceShellStream) {
+	data, err := ioutil.ReadFile("/etc/motd")
+	if err != nil {
+		// No MOTD. That's OK.
+		return
+	}
+	packet := tunnel.ServerShellPacket{Stdout: []byte(data)}
+	if err = s.Send(packet); err != nil {
+		vlog.Infof("Send failed: %v", err)
+	}
+}
+
+func setWindowSize(fd uintptr, row, col uint32) {
+	ws := lib.Winsize{Row: uint16(row), Col: uint16(col)}
+	if err := lib.SetWindowSize(fd, ws); err != nil {
+		vlog.Infof("Failed to set window size: %v", err)
+	}
+}
diff --git a/tunneld/impl/iomanager.go b/tunneld/impl/iomanager.go
new file mode 100644
index 0000000..fe422db
--- /dev/null
+++ b/tunneld/impl/iomanager.go
@@ -0,0 +1,113 @@
+package impl
+
+import (
+	"fmt"
+	"io"
+
+	"veyron/examples/tunnel"
+	"veyron2/vlog"
+)
+
+func runIOManager(stdin io.Writer, stdout, stderr io.Reader, ptyFd uintptr, stream tunnel.TunnelServiceShellStream) error {
+	m := ioManager{stdin: stdin, stdout: stdout, stderr: stderr, ptyFd: ptyFd, stream: stream}
+	return m.run()
+}
+
+// ioManager manages the forwarding of all the data between the shell and the
+// stream.
+type ioManager struct {
+	stdin          io.Writer
+	stdout, stderr io.Reader
+	ptyFd          uintptr
+	stream         tunnel.TunnelServiceShellStream
+
+	// done receives any error from chan2stream, user2stream, or
+	// stream2user.
+	done chan error
+	// outchan is used to serialize the output to the stream. This is
+	// needed because stream.Send is not thread-safe.
+	outchan chan tunnel.ServerShellPacket
+}
+
+func (m *ioManager) run() error {
+	// done receives any error from chan2stream, stdout2stream, or
+	// stream2stdin.
+	m.done = make(chan error, 3)
+
+	// outchan is used to serialize the output to the stream.
+	// chan2stream() receives data sent by stdout2outchan() and
+	// stderr2outchan() and sends it to the stream.
+	m.outchan = make(chan tunnel.ServerShellPacket)
+	defer close(m.outchan)
+	go m.chan2stream()
+
+	// Forward data between the shell's stdio and the stream.
+	go m.stdout2outchan()
+	if m.stderr != nil {
+		go m.stderr2outchan()
+	}
+	go m.stream2stdin()
+
+	// Block until something reports an error.
+	return <-m.done
+}
+
+// chan2stream receives ServerShellPacket from outchan and sends it to stream.
+func (m *ioManager) chan2stream() {
+	for packet := range m.outchan {
+		if err := m.stream.Send(packet); err != nil {
+			m.done <- err
+			return
+		}
+	}
+	m.done <- io.EOF
+}
+
+// stdout2stream reads data from the shell's stdout and sends it to the outchan.
+func (m *ioManager) stdout2outchan() {
+	for {
+		buf := make([]byte, 2048)
+		n, err := m.stdout.Read(buf[:])
+		if err != nil {
+			vlog.VI(2).Infof("stdout2outchan: %v", err)
+			m.done <- err
+			return
+		}
+		m.outchan <- tunnel.ServerShellPacket{Stdout: buf[:n]}
+	}
+}
+
+// stderr2stream reads data from the shell's stderr and sends it to the outchan.
+func (m *ioManager) stderr2outchan() {
+	for {
+		buf := make([]byte, 2048)
+		n, err := m.stderr.Read(buf[:])
+		if err != nil {
+			vlog.VI(2).Infof("stderr2outchan: %v", err)
+			m.done <- err
+			return
+		}
+		m.outchan <- tunnel.ServerShellPacket{Stderr: buf[:n]}
+	}
+}
+
+// stream2stdin reads data from the stream and sends it to the shell's stdin.
+func (m *ioManager) stream2stdin() {
+	for {
+		packet, err := m.stream.Recv()
+		if err != nil {
+			vlog.VI(2).Infof("stream2stdin: %v", err)
+			m.done <- err
+			return
+		}
+		if len(packet.Stdin) > 0 {
+			if n, err := m.stdin.Write(packet.Stdin); n != len(packet.Stdin) || err != nil {
+				m.done <- fmt.Errorf("stdin.Write returned (%d, %v) want (%d, nil)", n, err, len(packet.Stdin))
+				return
+			}
+		}
+		if packet.Rows > 0 && packet.Cols > 0 && m.ptyFd != 0 {
+			setWindowSize(m.ptyFd, packet.Rows, packet.Cols)
+		}
+	}
+}
diff --git a/tunneld/main.go b/tunneld/main.go
new file mode 100644
index 0000000..44aaf0e
--- /dev/null
+++ b/tunneld/main.go
@@ -0,0 +1,99 @@
+package main
+
+import (
+	"errors"
+	"flag"
+	"fmt"
+	"net"
+	"os"
+	"strings"
+
+	"veyron/examples/tunnel"
+	"veyron/examples/tunnel/tunneld/impl"
+	"veyron/lib/signals"
+	isecurity "veyron/runtimes/google/security"
+	"veyron2/ipc"
+	"veyron2/rt"
+	"veyron2/security"
+	"veyron2/vlog"
+)
+
+var (
+	protocol = flag.String("protocol", "tcp", "network to listen on. For example, set to 'veyron' and set --address to the endpoint/name of a proxy to have this tunnel service proxied.")
+	address  = flag.String("address", ":0", "address to listen on")
+
+	users = flag.String("users", "", "A comma-separated list of principal patterns allowed to use this service.")
+)
+
+// firstHardwareAddrInUse returns the hwaddr of the first network interface
+// that is up, excluding loopback.
+func firstHardwareAddrInUse() (string, error) {
+	interfaces, err := net.Interfaces()
+	if err != nil {
+		return "", err
+	}
+	for _, i := range interfaces {
+		if i.Name != "lo" && i.Flags&net.FlagUp != 0 {
+			name := i.HardwareAddr.String()
+			vlog.Infof("Using %q (from %v)", name, i.Name)
+			return name, nil
+		}
+	}
+	return "", errors.New("No usable network interfaces")
+}
+
+func authorizer() security.Authorizer {
+	ACL := make(security.ACL)
+	principals := strings.Split(*users, ",")
+	for _, p := range principals {
+		ACL[security.PrincipalPattern(p)] = security.LabelSet(security.AdminLabel)
+	}
+	return isecurity.NewACLAuthorizer(ACL)
+}
+
+func main() {
+	r := rt.Init()
+	defer r.Shutdown()
+	server, err := r.NewServer()
+	if err != nil {
+		vlog.Fatalf("NewServer failed: %v", err)
+	}
+	defer server.Stop()
+
+	if err := server.Register("", ipc.SoloDispatcher(tunnel.NewServerTunnel(&impl.T{}), authorizer())); err != nil {
+		vlog.Fatalf("Register failed: %v", err)
+	}
+	ep, err := server.Listen(*protocol, *address)
+	if err != nil {
+		vlog.Fatalf("Listen(%q, %q) failed: %v", "tcp", *address, err)
+	}
+	hwaddr, err := firstHardwareAddrInUse()
+	if err != nil {
+		vlog.Fatalf("Couldn't find a good hw address: %v", err)
+	}
+	hostname, err := os.Hostname()
+	if err != nil {
+		vlog.Fatalf("os.Hostname failed: %v", err)
+	}
+	// TODO(rthellend): This is not secure. We should use
+	// rt.R().Product().ID() and the associated verification, when it is
+	// ready.
+	names := []string{
+		fmt.Sprintf("tunnel/hostname/%s", hostname),
+		fmt.Sprintf("tunnel/hwaddr/%s", hwaddr),
+		fmt.Sprintf("tunnel/id/%s", rt.R().Identity().PublicID()),
+	}
+	published := false
+	for _, n := range names {
+		if err := server.Publish(n); err != nil {
+			vlog.Infof("Publish(%v) failed: %v", n, err)
+			continue
+		}
+		published = true
+	}
+	if !published {
+		vlog.Fatalf("Failed to publish with any of %v", names)
+	}
+	vlog.Infof("Listening on endpoint /%s (published as %v)", ep, names)
+	<-signals.ShutdownOnSignals()
+}
diff --git a/vsh/iomanager.go b/vsh/iomanager.go
new file mode 100644
index 0000000..00cd29d
--- /dev/null
+++ b/vsh/iomanager.go
@@ -0,0 +1,114 @@
+package main
+
+import (
+	"fmt"
+	"io"
+	"os"
+	"os/signal"
+	"syscall"
+
+	"veyron/examples/tunnel"
+	"veyron/examples/tunnel/lib"
+	"veyron2/vlog"
+)
+
+func runIOManager(stdin io.Reader, stdout, stderr io.Writer, stream tunnel.TunnelShellStream) error {
+	m := ioManager{stdin: stdin, stdout: stdout, stderr: stderr, stream: stream}
+	return m.run()
+}
+
+// ioManager manages the forwarding of all the data between the shell and the
+// stream.
+type ioManager struct {
+	stdin          io.Reader
+	stdout, stderr io.Writer
+	stream         tunnel.TunnelShellStream
+
+	// done receives any error from chan2stream, user2outchan, or
+	// stream2user.
+	done chan error
+	// outchan is used to serialize the output to the stream. This is
+	// needed because stream.Send is not thread-safe.
+	outchan chan tunnel.ClientShellPacket
+}
+
+func (m *ioManager) run() error {
+	m.done = make(chan error, 3)
+	// outchan is used to serialize the output to the stream.
+	// chan2stream() receives data sent by handleWindowResize() and
+	// user2outchan() and sends it to the stream.
+	m.outchan = make(chan tunnel.ClientShellPacket)
+	defer close(m.outchan)
+	go m.chan2stream()
+	// When the terminal window is resized, we receive a SIGWINCH. Then we
+	// send the new window size to the server.
+	winch := make(chan os.Signal, 1)
+	signal.Notify(winch, syscall.SIGWINCH)
+	defer signal.Stop(winch)
+	go m.handleWindowResize(winch)
+	// Forward data between the user and the remote shell.
+	go m.user2outchan()
+	go m.stream2user()
+	// Block until something reports an error.
+	return <-m.done
+}
+
+// chan2stream receives ClientShellPacket from outchan and sends it to stream.
+func (m *ioManager) chan2stream() {
+	for packet := range m.outchan {
+		if err := m.stream.Send(packet); err != nil {
+			m.done <- err
+			return
+		}
+	}
+	m.done <- io.EOF
+}
+
+func (m *ioManager) handleWindowResize(winch chan os.Signal) {
+	for _ = range winch {
+		ws, err := lib.GetWindowSize()
+		if err != nil {
+			vlog.Infof("GetWindowSize failed: %v", err)
+			continue
+		}
+		m.outchan <- tunnel.ClientShellPacket{Rows: uint32(ws.Row), Cols: uint32(ws.Col)}
+	}
+}
+
+// user2stream reads input from stdin and sends it to the outchan.
+func (m *ioManager) user2outchan() {
+	for {
+		buf := make([]byte, 2048)
+		n, err := m.stdin.Read(buf[:])
+		if err != nil {
+			vlog.VI(2).Infof("user2stream: %v", err)
+			m.done <- err
+			return
+		}
+		m.outchan <- tunnel.ClientShellPacket{Stdin: buf[:n]}
+	}
+}
+
+// stream2user reads data from the stream and sends it to either stdout or stderr.
+func (m *ioManager) stream2user() {
+	for {
+		packet, err := m.stream.Recv()
+		if err != nil {
+			vlog.VI(2).Infof("stream2user: %v", err)
+			m.done <- err
+			return
+		}
+		if len(packet.Stdout) > 0 {
+			if n, err := m.stdout.Write(packet.Stdout); n != len(packet.Stdout) || err != nil {
+				m.done <- fmt.Errorf("stdout.Write returned (%d, %v) want (%d, nil)", n, err, len(packet.Stdout))
+				return
+			}
+		}
+		if len(packet.Stderr) > 0 {
+			if n, err := m.stderr.Write(packet.Stderr); n != len(packet.Stderr) || err != nil {
+				m.done <- fmt.Errorf("stderr.Write returned (%d, %v) want (%d, nil)", n, err, len(packet.Stderr))
+				return
+			}
+		}
+	}
+}
diff --git a/vsh/main.go b/vsh/main.go
new file mode 100644
index 0000000..943835e
--- /dev/null
+++ b/vsh/main.go
@@ -0,0 +1,213 @@
+package main
+
+import (
+	"errors"
+	"flag"
+	"fmt"
+	"net"
+	"os"
+	"path"
+	"strings"
+	"time"
+
+	"veyron/examples/tunnel"
+	"veyron/examples/tunnel/lib"
+	"veyron/lib/signals"
+	"veyron2"
+	"veyron2/rt"
+	"veyron2/vlog"
+)
+
+var (
+	disablePty = flag.Bool("T", false, "Disable pseudo-terminal allocation.")
+	forcePty   = flag.Bool("t", false, "Force allocation of pseudo-terminal.")
+	vname      = flag.String("vname", "", "Veyron name (or endpoint) for tunneling service.")
+
+	portforward = flag.String("L", "", "localaddr,remoteaddr Forward local 'localaddr' to 'remoteaddr'")
+	lprotocol   = flag.String("local_protocol", "tcp", "Local network protocol for port forwarding")
+	rprotocol   = flag.String("remote_protocol", "tcp", "Remote network protocol for port forwarding")
+
+	noshell = flag.Bool("N", false, "Do not execute a shell. Only do port forwarding.")
+)
+
+func init() {
+	flag.Usage = func() {
+		bname := path.Base(os.Args[0])
+		fmt.Fprintf(os.Stderr, `%s: Veyron SHell.
+
+This tool is used to run shell commands or an interactive shell on a remote
+tunneld service.
+
+To open an interactive shell, use:
+  %s --host=<veyron name or endpoint>
+
+To run a shell command, use:
+  %s --host=<veyron name or endpoint> <command to run>
+
+The -L flag will forward connections from a local port to a remote address
+through the tunneld service. The flag value is localaddr,remoteaddr. E.g.
+  -L :14141,www.google.com:80
+
+%s can't be used directly with tools like rsync because veyron addresses don't
+look like traditional hostnames, which rsync doesn't understand. For
+compatibility with such tools, %s has a special feature that allows passing the
+veyron address via the VSH_NAME environment variable.
+
+  $ VSH_NAME=<veyron address> rsync -avh -e %s /foo/* veyron:/foo/
+
+In this example, the "veyron" host will be substituted with $VSH_NAME by %s
+and rsync will work as expected.
+
+Full flags:
+`, os.Args[0], bname, bname, bname, bname, os.Args[0], bname)
+		flag.PrintDefaults()
+	}
+}
+
+func main() {
+	// Work around the fact that os.Exit doesn't run deferred functions.
+	os.Exit(realMain())
+}
+
+func realMain() int {
+	r := rt.Init()
+	defer r.Shutdown()
+
+	host, cmd, err := veyronNameAndCommandLine()
+	if err != nil {
+		flag.Usage()
+		fmt.Fprintf(os.Stderr, "\n%v\n", err)
+		return 1
+	}
+
+	t, err := tunnel.BindTunnel(host)
+	if err != nil {
+		vlog.Fatalf("BindTunnel(%q) failed: %v", host, err)
+	}
+
+	if len(*portforward) > 0 {
+		go runPortForwarding(t, host)
+	}
+
+	if *noshell {
+		<-signals.ShutdownOnSignals()
+		return 0
+	}
+
+	opts := shellOptions(cmd)
+
+	stream, err := t.Shell(cmd, opts, veyron2.CallTimeout(24*time.Hour))
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
+		return 1
+	}
+	saved := lib.EnterRawTerminalMode()
+	defer lib.RestoreTerminalSettings(saved)
+	runIOManager(os.Stdin, os.Stdout, os.Stderr, stream)
+
+	exitMsg := fmt.Sprintf("Connection to %s closed.", host)
+	exitStatus, err := stream.Finish()
+	if err != nil {
+		exitMsg += fmt.Sprintf(" (%v)", err)
+	}
+	vlog.VI(1).Info(exitMsg)
+	// Only show the exit message on stdout for interactive shells.
+	// Otherwise, the exit message might get confused with the output
+	// of the command that was run.
+	if err != nil {
+		fmt.Fprintln(os.Stderr, exitMsg)
+	} else if len(cmd) == 0 {
+		fmt.Println(exitMsg)
+	}
+	return int(exitStatus)
+}
+
+func shellOptions(cmd string) (opts tunnel.ShellOpts) {
+	opts.UsePty = (len(cmd) == 0 || *forcePty) && !*disablePty
+	opts.Environment = environment()
+	ws, err := lib.GetWindowSize()
+	if err != nil {
+		vlog.VI(1).Infof("GetWindowSize failed: %v", err)
+	} else {
+		opts.Rows = uint32(ws.Row)
+		opts.Cols = uint32(ws.Col)
+	}
+	return
+}
+
+func environment() []string {
+	env := []string{}
+	for _, name := range []string{"TERM", "COLORTERM"} {
+		if value := os.Getenv(name); value != "" {
+			env = append(env, fmt.Sprintf("%s=%s", name, value))
+		}
+	}
+	return env
+}
+
+// veyronNameAndCommandLine extracts the veyron name and the remote command to
+// send to the server. The name can be specified with the --vname flag or as the
+// first non-flag argument. The command line is the concatenation of all the
+// non-flag arguments, minus the veyron name.
+func veyronNameAndCommandLine() (string, string, error) {
+	name := *vname
+	args := flag.Args()
+	if len(name) == 0 {
+		if len(args) > 0 {
+			name = args[0]
+			args = args[1:]
+		}
+	}
+	if len(name) == 0 {
+		return "", "", errors.New("veyron name missing")
+	}
+	// For compatibility with tools like rsync. Because veyron addresses
+	// don't look like traditional hostnames, tools that work with rsh and
+	// ssh can't work directly with vsh. This trick makes the following
+	// possible:
+	//   $ VSH_NAME=<veyron address> rsync -avh -e vsh /foo/* veyron:/foo/
+	// The "veyron" host will be substituted with <veyron address>.
+	if envName := os.Getenv("VSH_NAME"); len(envName) > 0 && name == "veyron" {
+		name = envName
+	}
+	cmd := strings.Join(args, " ")
+	return name, cmd, nil
+}
+
+func runPortForwarding(t tunnel.Tunnel, host string) {
+	// *portforward is localaddr,remoteaddr
+	parts := strings.Split(*portforward, ",")
+	var laddr, raddr string
+	if len(parts) != 2 {
+		vlog.Fatalf("-L flag expects 2 values separated by a comma")
+	}
+	laddr = parts[0]
+	raddr = parts[1]
+
+	ln, err := net.Listen(*lprotocol, laddr)
+	if err != nil {
+		vlog.Fatalf("net.Listen(%q, %q) failed: %v", *lprotocol, laddr, err)
+	}
+	defer ln.Close()
+	vlog.VI(1).Infof("Listening on %q", ln.Addr())
+	for {
+		conn, err := ln.Accept()
+		if err != nil {
+			vlog.Infof("Accept failed: %v", err)
+			continue
+		}
+		stream, err := t.Forward(*rprotocol, raddr, veyron2.CallTimeout(24*time.Hour))
+		if err != nil {
+			vlog.Infof("Tunnel(%q, %q) failed: %v", *rprotocol, raddr, err)
+			conn.Close()
+			continue
+		}
+		name := fmt.Sprintf("%v-->%v-->(%v)-->%v", conn.RemoteAddr(), conn.LocalAddr(), host, raddr)
+		go func() {
+			vlog.VI(1).Infof("TUNNEL START: %v", name)
+			errf := lib.Forward(conn, stream)
+			err := stream.Finish()
+			vlog.VI(1).Infof("TUNNEL END  : %v (%v, %v)", name, errf, err)
+		}()
+	}
+}