Merge "services/device/device: refactor {instance|installation}StateFlag with 'generic'"
diff --git a/services/agent/internal/ipc/ipc.go b/services/agent/internal/ipc/ipc.go
new file mode 100644
index 0000000..000d716
--- /dev/null
+++ b/services/agent/internal/ipc/ipc.go
@@ -0,0 +1,408 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package ipc implements a simple IPC system based on VOM.
+package ipc
+
+import (
+ "fmt"
+ "io"
+ "net"
+ "reflect"
+ "sync"
+ "v.io/v23/vom"
+ "v.io/x/lib/vlog"
+ "v.io/x/ref/services/agent"
+)
+
+const currentVersion = 1
+
+type decoderFunc func(n uint32, dec *vom.Decoder) ([]reflect.Value, error)
+
+type rpcInfo struct {
+ id uint64
+ results []interface{}
+ done chan error
+}
+
+type rpcHandler struct {
+ decode decoderFunc
+ f reflect.Value
+}
+
+// IPC provides interprocess rpcs.
+// One process must act as the "server" by listening for connections.
+// However once connected rpcs can flow in either direction.
+type IPC struct {
+ handlers map[string]rpcHandler
+ mu sync.Mutex
+ listener *net.UnixListener
+ conns []*IPCConn
+}
+
+// IPCConn represents a connection to a process.
+// It can be used to make rpcs to the other process. It also
+// dispatches rpcs from that process to handlers registered with the
+// parent IPC object.
+type IPCConn struct {
+ enc *vom.Encoder
+ dec *vom.Decoder
+ conn net.Conn
+ mu sync.Mutex
+ nextId uint64
+ ipc *IPC
+ rpcs map[uint64]rpcInfo
+}
+
+// Close the connection to the process.
+// All outstanding rpcs will return errors.
+func (c *IPCConn) Close() {
+ c.ipc.closeConn(c)
+ c.mu.Lock()
+ for _, rpc := range c.rpcs {
+ rpc.done <- io.EOF
+ }
+ c.rpcs = nil
+ c.mu.Unlock()
+}
+
+// Perform an rpc with the given name and arguments.
+// 'args' must correspond with the method registered in the other process,
+// and results must contain pointers to the return types of the method.
+// All rpc methods must have a final error result, which is not included in 'results'.
+// It is the return value of Call().
+func (c *IPCConn) Call(method string, args []interface{}, results ...interface{}) error {
+ ch := c.ipc.startCall(c, method, args, results)
+ return <-ch
+}
+
+// Create a new IPC object.
+// After calling new you can register handlers with Serve().
+// Then call Listen() or Connect().
+func NewIPC() *IPC {
+ ipc := new(IPC)
+ ipc.handlers = make(map[string]rpcHandler)
+ return ipc
+}
+
+// Listen for connections by creating a UNIX domain socket at 'path'.
+func (ipc *IPC) Listen(path string) error {
+ addr, err := net.ResolveUnixAddr("unix", path)
+ if err != nil {
+ return err
+ }
+ ipc.mu.Lock()
+ ipc.listener, err = net.ListenUnix("unix", addr)
+ ipc.mu.Unlock()
+ if err == nil {
+ go ipc.listenLoop()
+ }
+ return err
+}
+
+// Connect to a listening socket at 'path'.
+func (ipc *IPC) Connect(path string) (*IPCConn, error) {
+ conn, err := net.Dial("unix", path)
+ if err != nil {
+ return nil, err
+ }
+ ipc.mu.Lock()
+ ic := ipc.newConnLocked(conn)
+ ipc.mu.Unlock()
+ go ipc.readLoop(ic)
+ return ic, nil
+}
+
+// Connections returns all the current connections in this IPC.
+func (ipc *IPC) Connections() []*IPCConn {
+ defer ipc.mu.Unlock()
+ ipc.mu.Lock()
+ conns := make([]*IPCConn, len(ipc.conns))
+ copy(conns, ipc.conns)
+ return conns
+}
+
+// Must be called while holding ipc.mu
+func (ipc *IPC) newConnLocked(conn net.Conn) *IPCConn {
+ result := &IPCConn{enc: vom.NewEncoder(conn), dec: vom.NewDecoder(conn), conn: conn, ipc: ipc, rpcs: make(map[uint64]rpcInfo)}
+ // Don't allow any rpcs to be sent until negotiateVersion unlocks this.
+ result.mu.Lock()
+ ipc.conns = append(ipc.conns, result)
+ return result
+}
+
+func (ipc *IPC) closeConn(c *IPCConn) {
+ ipc.mu.Lock()
+ for i := range ipc.conns {
+ if ipc.conns[i] == c {
+ last := len(ipc.conns) - 1
+ ipc.conns[i], ipc.conns[last], ipc.conns = ipc.conns[last], nil, ipc.conns[:last]
+ break
+ }
+ }
+ ipc.mu.Unlock()
+ c.conn.Close()
+}
+
+func (ipc *IPC) listenLoop() {
+ ipc.mu.Lock()
+ l := ipc.listener
+ ipc.mu.Unlock()
+ for l != nil {
+ conn, err := l.AcceptUnix()
+ if err != nil {
+ vlog.VI(3).Infof("Accept error: %v", err)
+ return
+ }
+ ipc.mu.Lock()
+ l = ipc.listener
+ if l == nil {
+ // We're already closed!
+ conn.Close()
+ return
+ }
+ ic := ipc.newConnLocked(conn)
+ ipc.mu.Unlock()
+ go ipc.readLoop(ic)
+ }
+}
+
+func (ipc *IPC) readLoop(c *IPCConn) {
+ v := ipc.negotiateVersion(c)
+ if v != currentVersion {
+ ipc.closeConn(c)
+ return
+ }
+ for {
+ if f, err := ipc.readMessage(c); err != nil {
+ vlog.VI(3).Infof("ipc read error: %v", err)
+ c.Close()
+ return
+ } else if f != nil {
+ go f()
+ }
+ }
+}
+
+func (ipc *IPC) negotiateVersion(c *IPCConn) int32 {
+ go func() {
+ myInfo := agent.ConnInfo{MinVersion: currentVersion, MaxVersion: currentVersion}
+ // c.mu.Lock() already called in newConnLocked
+ vlog.VI(4).Infof("negotiateVersion: sending %v", myInfo)
+ err := c.enc.Encode(myInfo)
+ c.mu.Unlock()
+ if err != nil {
+ vlog.VI(3).Infof("ipc.negotiateVersion encode: %v", err)
+ ipc.closeConn(c)
+ }
+ }()
+ var theirInfo agent.ConnInfo
+ err := c.dec.Decode(&theirInfo)
+ if err != nil {
+ vlog.VI(3).Infof("ipc.negotiateVersion decode: %v", err)
+ }
+ if theirInfo.MinVersion <= currentVersion && theirInfo.MaxVersion >= currentVersion {
+ return currentVersion
+ }
+ vlog.VI(3).Infof("ipc.negotiateVersion %d not in remote range: %d-%d", currentVersion, theirInfo.MinVersion, theirInfo.MaxVersion)
+ return -1
+}
+
+func makeDecoder(t reflect.Type) decoderFunc {
+ numArgs := t.NumIn() - 1
+ inTypes := make([]reflect.Type, numArgs)
+ for i := 1; i < numArgs+1; i++ {
+ inTypes[i-1] = t.In(i)
+ }
+ return func(n uint32, dec *vom.Decoder) (result []reflect.Value, err error) {
+ if n != uint32(numArgs) {
+ for i := uint32(0); i < n; i++ {
+ if err = dec.Ignore(); err != nil {
+ return nil, err
+ }
+ }
+ return nil, fmt.Errorf("wrong number of args: expected %d, got %d", numArgs, n)
+ }
+
+ result = make([]reflect.Value, numArgs)
+ for i := 0; i < numArgs; i++ {
+ v := reflect.New(inTypes[i])
+ result[i] = reflect.Indirect(v)
+ if err = dec.Decode(v.Interface()); err != nil {
+ vlog.VI(3).Infof("decode error for %v: %v", inTypes[i], err)
+ return nil, err
+ }
+ }
+ return
+ }
+}
+
+func noDecoder(n uint32, dec *vom.Decoder) (result []reflect.Value, err error) {
+ for i := uint32(0); i < n; i++ {
+ if err = dec.Ignore(); err != nil {
+ return nil, err
+ }
+ }
+ return nil, fmt.Errorf("unknown method")
+}
+
+// Serve registers rpc handlers.
+// All exported methods in 'x' are registered for rpc.
+// All arguments and results for these methods must be VOM serializable.
+// Additionally each method must have at least one return value, and
+// the final return value must be an 'error'.
+// Serve must be called before Listen() or Connect().
+func (ipc *IPC) Serve(x interface{}) error {
+ v := reflect.ValueOf(x)
+ t := reflect.TypeOf(x)
+ for i, numMethods := 0, v.NumMethod(); i < numMethods; i++ {
+ m := t.Method(i)
+ if _, exists := ipc.handlers[m.Name]; exists {
+ return fmt.Errorf("Method %s already registered", m.Name)
+ }
+ ipc.handlers[m.Name] = rpcHandler{makeDecoder(m.Type), v.Method(i)}
+ vlog.VI(4).Infof("Registered method %q", m.Name)
+ }
+ return nil
+}
+
+func (ipc *IPC) dispatch(req agent.RpcRequest) rpcHandler {
+ handler, ok := ipc.handlers[req.Method]
+ if !ok {
+ handler = rpcHandler{noDecoder, reflect.Value{}}
+ }
+ return handler
+}
+
+// Close the IPC and all it's connections.
+func (ipc *IPC) Close() {
+ ipc.mu.Lock()
+ if ipc.listener != nil {
+ ipc.listener.Close()
+ ipc.listener = nil
+ }
+ for _, c := range ipc.conns {
+ c.conn.Close()
+ }
+ ipc.conns = nil
+ ipc.mu.Unlock()
+}
+
+func (ipc *IPC) startCall(c *IPCConn, method string, args []interface{}, results []interface{}) (ch chan error) {
+ ch = make(chan error, 1)
+ header := agent.RpcMessageReq{agent.RpcRequest{Method: method, NumArgs: uint32(len(args))}}
+ shouldClose := false
+ defer func() {
+ // Only close c after we unlock it.
+ if shouldClose {
+ c.Close()
+ }
+ }()
+ defer c.mu.Unlock()
+ c.mu.Lock()
+ header.Value.Id = c.nextId
+ c.nextId++
+ vlog.VI(4).Infof("startCall sending %v", header)
+ if err := c.enc.Encode(header); err != nil {
+ // TODO: Close?
+ vlog.VI(4).Infof("startCall encode error %v", err)
+ ch <- err
+ return
+ }
+ for _, arg := range args {
+ if err := c.enc.Encode(arg); err != nil {
+ vlog.VI(4).Infof("startCall arg error %v", err)
+ shouldClose = true
+ ch <- err
+ return
+ }
+ }
+ c.rpcs[header.Value.Id] = rpcInfo{header.Value.Id, results, ch}
+ return
+}
+
+func (ipc *IPC) readMessage(c *IPCConn) (func(), error) {
+ var m agent.RpcMessage
+ if err := c.dec.Decode(&m); err != nil {
+ return nil, err
+ }
+ vlog.VI(4).Infof("readMessage decoded %v", m)
+ switch msg := m.Interface().(type) {
+ case agent.RpcRequest:
+ handler := ipc.dispatch(msg)
+ args, err := handler.decode(msg.NumArgs, c.dec)
+
+ if err != nil {
+ err = fmt.Errorf("%s: %v", msg.Method, err)
+ }
+ return func() { ipc.handleReq(c, msg.Id, handler.f, args, err) }, nil
+ case agent.RpcResponse:
+ return nil, ipc.handleResp(c, msg)
+ default:
+ return nil, fmt.Errorf("unsupported type %t", msg)
+ }
+}
+
+func (ipc *IPC) handleReq(c *IPCConn, id uint64, method reflect.Value, args []reflect.Value, err error) {
+ var results []reflect.Value
+ if err == nil {
+ results = method.Call(args)
+ last := len(results) - 1
+ if !results[last].IsNil() {
+ err = results[last].Interface().(error)
+ }
+ results = results[:last]
+ }
+ header := agent.RpcMessageResp{Value: agent.RpcResponse{Id: id, NumArgs: uint32(len(results)), Err: err}}
+ shouldClose := false
+ defer func() {
+ if shouldClose {
+ c.Close()
+ }
+ }()
+ defer c.mu.Unlock()
+ c.mu.Lock()
+ vlog.VI(4).Infof("handleReq sending %v", header)
+ if err = c.enc.Encode(header); err != nil {
+ vlog.VI(3).Infof("handleReq error %v", err)
+ shouldClose = true
+ return
+ }
+ for _, result := range results {
+ if err = c.enc.Encode(result.Interface()); err != nil {
+ vlog.VI(3).Infof("handleReq result error %v", err)
+ shouldClose = true
+ return
+ }
+ }
+}
+
+func (ipc *IPC) handleResp(c *IPCConn, resp agent.RpcResponse) error {
+ c.mu.Lock()
+ info, ok := c.rpcs[resp.Id]
+ if ok {
+ delete(c.rpcs, resp.Id)
+ }
+ c.mu.Unlock()
+ if ok && resp.NumArgs == uint32(len(info.results)) {
+ for i := range info.results {
+ if err := c.dec.Decode(info.results[i]); err != nil {
+ vlog.VI(3).Infof("result decode error for %T: %v", info.results[i], err)
+ info.done <- err
+ return err
+ }
+ }
+ info.done <- resp.Err
+ } else {
+ for i := uint32(0); i < resp.NumArgs; i++ {
+ c.dec.Ignore()
+ }
+ err := resp.Err
+ if err == nil {
+ err = fmt.Errorf("invalid results")
+ }
+ info.done <- err
+ }
+ return nil
+}
diff --git a/services/agent/internal/ipc/ipc_test.go b/services/agent/internal/ipc/ipc_test.go
new file mode 100644
index 0000000..4c553eb
--- /dev/null
+++ b/services/agent/internal/ipc/ipc_test.go
@@ -0,0 +1,309 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ipc
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+ "v.io/v23/vom"
+ "v.io/x/lib/vlog"
+ "v.io/x/ref/services/agent"
+)
+
+type echo int
+
+func (e echo) Echo(m string) (string, error) {
+ return m, nil
+}
+
+type delayedEcho struct {
+ gotA, gotB chan struct{}
+ doneA, doneB chan string
+}
+
+func newDelayedEcho() delayedEcho {
+ return delayedEcho{make(chan struct{}, 1), make(chan struct{}, 1), make(chan string, 1), make(chan string, 1)}
+}
+
+func (d delayedEcho) EchoA() (string, error) {
+ close(d.gotA)
+ return <-d.doneA, nil
+}
+
+func (d delayedEcho) EchoB(prefix string) (string, error) {
+ close(d.gotB)
+ return prefix + <-d.doneB, nil
+}
+
+type returnError int
+
+func (returnError) BreakMe(a, b string) (int, error) {
+ return 0, fmt.Errorf("%s %s", a, b)
+}
+
+type tunnel struct {
+ ipc *IPC
+}
+
+func (t *tunnel) Tunnel() (str string, err error) {
+ conn := t.ipc.Connections()[0]
+ err = conn.Call("Echo", []interface{}{"Tunnel"}, &str)
+ return
+}
+
+func newServer(t *testing.T, servers ...interface{}) (ipc *IPC, path string, f func()) {
+ dir, err := ioutil.TempDir("", "sock")
+ if err != nil {
+ t.Fatal(err)
+ }
+ path = filepath.Join(dir, "sock")
+ ipc = NewIPC()
+ for _, s := range servers {
+ if err := ipc.Serve(s); err != nil {
+ t.Fatal(err)
+ }
+ }
+ if err := ipc.Listen(path); err != nil {
+ os.RemoveAll(dir)
+ t.Fatal(err)
+ }
+ return ipc, path, func() { ipc.Close(); os.RemoveAll(dir) }
+}
+
+func TestDoubleServe(t *testing.T) {
+ ipc := NewIPC()
+ var a echo
+ var b echo
+ if err := ipc.Serve(a); err != nil {
+ t.Fatal(err)
+ }
+ if err := ipc.Serve(b); err == nil {
+ t.Fatal("Expected error")
+ }
+}
+
+func TestListen(t *testing.T) {
+ ipc1, path, cleanup := newServer(t)
+ defer cleanup()
+ if stat, err := os.Stat(path); err == nil {
+ if stat.Mode()&os.ModeSocket == 0 {
+ t.Fatalf("Not a socket: %#o", stat.Mode())
+ }
+ } else {
+ t.Fatal(err)
+ }
+ ipc1.Close()
+ if _, err := os.Stat(path); !os.IsNotExist(err) {
+ t.Fatalf("Expected NotExist, got %v", err)
+ }
+}
+
+func TestBadConnect(t *testing.T) {
+ _, path, cleanup := newServer(t)
+ defer cleanup()
+ conn, err := net.Dial("unix", path)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ enc := vom.NewEncoder(conn)
+ dec := vom.NewDecoder(conn)
+
+ var theirInfo agent.ConnInfo
+ if err = dec.Decode(&theirInfo); err != nil {
+ t.Fatal(err)
+ }
+
+ if err = enc.Encode(agent.RpcMessageReq{agent.RpcRequest{Id: 0, Method: "foo", NumArgs: 0}}); err != nil {
+ t.Fatal(err)
+ }
+ var response agent.RpcMessage
+ if err = dec.Decode(&response); err != io.EOF {
+ t.Fatalf("Expected eof, got %v", err)
+ }
+}
+
+func TestBadMinVersion(t *testing.T) {
+ _, path, cleanup := newServer(t)
+ defer cleanup()
+ conn, err := net.Dial("unix", path)
+ if err != nil {
+ t.Fatal(err)
+ }
+ enc := vom.NewEncoder(conn)
+ dec := vom.NewDecoder(conn)
+
+ var info agent.ConnInfo
+ if err = dec.Decode(&info); err != nil {
+ t.Fatal(err)
+ }
+
+ info.MaxVersion++
+ info.MinVersion = info.MaxVersion
+ if err = enc.Encode(info); err != nil {
+ t.Fatal(err)
+ }
+
+ if err = enc.Encode(agent.RpcMessageReq{agent.RpcRequest{Id: 0, Method: "foo", NumArgs: 0}}); err != nil {
+ if err != io.EOF {
+ t.Fatal(err)
+ }
+ }
+ var response agent.RpcMessage
+ if err = dec.Decode(&response); err != io.EOF {
+ t.Fatalf("Expected EOF, got %v", err)
+ }
+}
+
+func TestBadMaxVersion(t *testing.T) {
+ _, path, cleanup := newServer(t)
+ defer cleanup()
+ conn, err := net.Dial("unix", path)
+ if err != nil {
+ t.Fatal(err)
+ }
+ enc := vom.NewEncoder(conn)
+ dec := vom.NewDecoder(conn)
+
+ var info agent.ConnInfo
+ if err = dec.Decode(&info); err != nil {
+ t.Fatal(err)
+ }
+
+ info.MinVersion--
+ info.MaxVersion = info.MinVersion
+ if err = enc.Encode(info); err != nil {
+ t.Fatal(err)
+ }
+
+ if err = enc.Encode(agent.RpcMessageReq{agent.RpcRequest{Id: 0, Method: "foo", NumArgs: 0}}); err != nil {
+ if err != io.EOF {
+ t.Fatal(err)
+ }
+ }
+ var response agent.RpcMessage
+ if err = dec.Decode(&response); err != io.EOF {
+ t.Fatalf("Expected EOF, got %v", err)
+ }
+}
+
+func TestEcho(t *testing.T) {
+ var e echo
+ _, path, cleanup := newServer(t, e)
+ defer cleanup()
+
+ ipc2 := NewIPC()
+ defer ipc2.Close()
+ client, err := ipc2.Connect(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var result string
+ err = client.Call("Echo", []interface{}{"hello"}, &result)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if result != "hello" {
+ t.Fatalf("Expected hello, got %q", result)
+ }
+}
+
+func TestOutOfOrder(t *testing.T) {
+ delay := newDelayedEcho()
+ _, path, cleanup := newServer(t, delay)
+ defer cleanup()
+
+ ipc2 := NewIPC()
+ defer ipc2.Close()
+ client, err := ipc2.Connect(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var resultA string
+ errA := make(chan error)
+ go func() { errA <- client.Call("EchoA", nil, &resultA) }()
+ <-delay.gotA
+
+ delay.doneB <- " world"
+ var resultB string
+ err = client.Call("EchoB", []interface{}{"hello"}, &resultB)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if resultB != "hello world" {
+ t.Fatalf("Expected 'hello word', got %q", resultB)
+ }
+ delay.doneA <- "foobar"
+ if err := <-errA; err != nil {
+ t.Fatal(err)
+ }
+ if resultA != "foobar" {
+ t.Fatalf("Expected foobar, got %q", resultA)
+ }
+
+}
+
+func TestErrorReturn(t *testing.T) {
+ var s returnError
+ _, path, cleanup := newServer(t, s)
+ defer cleanup()
+
+ ipc2 := NewIPC()
+ defer ipc2.Close()
+ client, err := ipc2.Connect(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var result int
+ err = client.Call("BreakMe", []interface{}{"oh", "no"}, &result)
+ if err == nil {
+ t.Fatalf("Expected error, got %d", result)
+ }
+ if !strings.HasSuffix(err.Error(), "oh no") {
+ t.Fatalf("Expected 'oh no', got %q", err.Error())
+ }
+}
+
+func TestBidi(t *testing.T) {
+ s := &tunnel{}
+ ipc1, path, cleanup := newServer(t, s)
+ defer cleanup()
+ s.ipc = ipc1
+
+ ipc2 := NewIPC()
+ defer ipc2.Close()
+
+ var e echo
+ ipc2.Serve(e)
+ client, err := ipc2.Connect(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var result string
+ err = client.Call("Tunnel", nil, &result)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if result != "Tunnel" {
+ t.Fatalf("Expected Tunnel, got %q", result)
+ }
+}
+
+func TestMain(m *testing.M) {
+ flag.Parse()
+ vlog.ConfigureLibraryLoggerFromFlags()
+ os.Exit(m.Run())
+}
diff --git a/services/agent/wire.vdl b/services/agent/wire.vdl
index 1d92fcc..b26c22d 100644
--- a/services/agent/wire.vdl
+++ b/services/agent/wire.vdl
@@ -67,3 +67,24 @@
// recieving a new item.
NotifyWhenChanged() stream<_, bool> error
}
+
+type ConnInfo struct {
+ MinVersion, MaxVersion int32
+}
+
+type RpcRequest struct {
+ Id uint64
+ Method string
+ NumArgs uint32
+}
+
+type RpcResponse struct {
+ Id uint64
+ Err error
+ NumArgs uint32
+}
+
+type RpcMessage union {
+ Req RpcRequest
+ Resp RpcResponse
+}
diff --git a/services/agent/wire.vdl.go b/services/agent/wire.vdl.go
index da18067..d3d1bd6 100644
--- a/services/agent/wire.vdl.go
+++ b/services/agent/wire.vdl.go
@@ -41,11 +41,88 @@
"v.io/v23"
"v.io/v23/context"
"v.io/v23/rpc"
+ "v.io/v23/vdl"
// VDL user imports
"v.io/v23/security"
)
+type ConnInfo struct {
+ MinVersion int32
+ MaxVersion int32
+}
+
+func (ConnInfo) __VDLReflect(struct {
+ Name string `vdl:"v.io/x/ref/services/agent.ConnInfo"`
+}) {
+}
+
+type RpcRequest struct {
+ Id uint64
+ Method string
+ NumArgs uint32
+}
+
+func (RpcRequest) __VDLReflect(struct {
+ Name string `vdl:"v.io/x/ref/services/agent.RpcRequest"`
+}) {
+}
+
+type RpcResponse struct {
+ Id uint64
+ Err error
+ NumArgs uint32
+}
+
+func (RpcResponse) __VDLReflect(struct {
+ Name string `vdl:"v.io/x/ref/services/agent.RpcResponse"`
+}) {
+}
+
+type (
+ // RpcMessage represents any single field of the RpcMessage union type.
+ RpcMessage interface {
+ // Index returns the field index.
+ Index() int
+ // Interface returns the field value as an interface.
+ Interface() interface{}
+ // Name returns the field name.
+ Name() string
+ // __VDLReflect describes the RpcMessage union type.
+ __VDLReflect(__RpcMessageReflect)
+ }
+ // RpcMessageReq represents field Req of the RpcMessage union type.
+ RpcMessageReq struct{ Value RpcRequest }
+ // RpcMessageResp represents field Resp of the RpcMessage union type.
+ RpcMessageResp struct{ Value RpcResponse }
+ // __RpcMessageReflect describes the RpcMessage union type.
+ __RpcMessageReflect struct {
+ Name string `vdl:"v.io/x/ref/services/agent.RpcMessage"`
+ Type RpcMessage
+ Union struct {
+ Req RpcMessageReq
+ Resp RpcMessageResp
+ }
+ }
+)
+
+func (x RpcMessageReq) Index() int { return 0 }
+func (x RpcMessageReq) Interface() interface{} { return x.Value }
+func (x RpcMessageReq) Name() string { return "Req" }
+func (x RpcMessageReq) __VDLReflect(__RpcMessageReflect) {}
+
+func (x RpcMessageResp) Index() int { return 1 }
+func (x RpcMessageResp) Interface() interface{} { return x.Value }
+func (x RpcMessageResp) Name() string { return "Resp" }
+func (x RpcMessageResp) __VDLReflect(__RpcMessageReflect) {}
+
+func init() {
+ vdl.Register((*ConnInfo)(nil))
+ vdl.Register((*RpcRequest)(nil))
+ vdl.Register((*RpcResponse)(nil))
+ vdl.Register((*RpcMessage)(nil))
+}
+
// AgentClientMethods is the client interface
// containing Agent methods.
type AgentClientMethods interface {