blob: 39ccdc2edf290d498f9bd75a9d06205e41b091a4 [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Todd Wang88509682015-04-10 10:28:24 -07005// Package agentlib implements a client for communicating with an agentd process
6// holding the private key for an identity.
7package agentlib
Ryan Brownfed691e2014-09-15 13:09:40 -07008
9import (
10 "fmt"
Ryan Brown0a3e28a2015-08-12 14:59:14 -070011 "io"
Ryan Brown50b473a2014-09-23 14:23:00 -070012 "net"
13 "os"
Ryan Brown7f950a82015-04-20 18:08:39 -070014 "strconv"
Ryan Brown0a3e28a2015-08-12 14:59:14 -070015 "sync"
Ryan Brown7f950a82015-04-20 18:08:39 -070016 "syscall"
Ryan Brownfed691e2014-09-15 13:09:40 -070017
Jiri Simsa6ac95222015-02-23 16:11:49 -080018 "v.io/v23/context"
Jiri Simsa6ac95222015-02-23 16:11:49 -080019 "v.io/v23/naming"
20 "v.io/v23/options"
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070021 "v.io/v23/rpc"
Jiri Simsa6ac95222015-02-23 16:11:49 -080022 "v.io/v23/security"
Ryan Brown7f950a82015-04-20 18:08:39 -070023 "v.io/v23/verror"
Jiri Simsa6ac95222015-02-23 16:11:49 -080024 "v.io/v23/vtrace"
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -070025 "v.io/x/ref/internal/logger"
Ryan Brown0a3e28a2015-08-12 14:59:14 -070026 "v.io/x/ref/services/agent"
Todd Wangb3511492015-04-07 23:32:34 -070027 "v.io/x/ref/services/agent/internal/cache"
Ryan Brown0a3e28a2015-08-12 14:59:14 -070028 "v.io/x/ref/services/agent/internal/ipc"
Todd Wangfb939032015-04-08 16:42:44 -070029 "v.io/x/ref/services/agent/internal/unixfd"
Ryan Brownfed691e2014-09-15 13:09:40 -070030)
31
Ryan Brown7f950a82015-04-20 18:08:39 -070032const pkgPath = "v.io/x/ref/services/agent/agentlib"
33
34// Errors
35var (
36 errInvalidProtocol = verror.Register(pkgPath+".errInvalidProtocol",
37 verror.NoRetry, "{1:}{2:} invalid agent protocol {3}")
38)
Ryan Brownfed691e2014-09-15 13:09:40 -070039
40type client struct {
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -070041 caller caller
Ryan Brownfed691e2014-09-15 13:09:40 -070042 key security.PublicKey
43}
44
Ryan Brown0a3e28a2015-08-12 14:59:14 -070045type caller interface {
46 call(name string, results []interface{}, args ...interface{}) error
47 io.Closer
48}
49
50type ipcCaller struct {
51 conn *ipc.IPCConn
52 flush func()
53 mu sync.Mutex
54}
55
56func (i *ipcCaller) call(name string, results []interface{}, args ...interface{}) error {
57 return i.conn.Call(name, args, results...)
58}
59
60func (i *ipcCaller) Close() error {
61 i.conn.Close()
62 return nil
63}
64
65func (i *ipcCaller) FlushAllCaches() error {
66 var flush func()
67 i.mu.Lock()
68 flush = i.flush
69 i.mu.Unlock()
70 if flush != nil {
71 flush()
72 }
73 return nil
74}
75
76type vrpcCaller struct {
Matt Rosencrantz6edab562015-01-12 11:07:55 -080077 ctx *context.T
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070078 client rpc.Client
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -070079 name string
Ryan Brown0a3e28a2015-08-12 14:59:14 -070080 cancel func()
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -070081}
82
Ryan Brown0a3e28a2015-08-12 14:59:14 -070083func (c *vrpcCaller) Close() error {
84 c.cancel()
85 return nil
86}
87
88func (c *vrpcCaller) call(name string, results []interface{}, args ...interface{}) error {
Todd Wange77f9952015-02-18 13:20:50 -080089 call, err := c.startCall(name, args...)
90 if err != nil {
91 return err
Ryan Brownfed691e2014-09-15 13:09:40 -070092 }
Todd Wange77f9952015-02-18 13:20:50 -080093 if err := call.Finish(results...); err != nil {
94 return err
95 }
96 return nil
Ryan Brownfed691e2014-09-15 13:09:40 -070097}
98
Ryan Brown0a3e28a2015-08-12 14:59:14 -070099func (c *vrpcCaller) startCall(name string, args ...interface{}) (rpc.ClientCall, error) {
Todd Wangad492042015-04-17 15:58:40 -0700100 ctx, _ := vtrace.WithNewTrace(c.ctx)
Suharsh Sivakumar2c5d8102015-03-23 08:49:12 -0700101 // SecurityNone is safe here since we're using anonymous unix sockets.
102 return c.client.StartCall(ctx, c.name, name, args, options.SecurityNone, options.NoResolve{})
Ryan Brown81bcb3a2015-02-11 10:58:01 -0800103}
104
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700105func results(inputs ...interface{}) []interface{} {
Todd Wange77f9952015-02-18 13:20:50 -0800106 return inputs
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700107}
108
Ryan Brown0a3e28a2015-08-12 14:59:14 -0700109func newUncachedPrincipalX(path string) (*client, error) {
110 caller := new(ipcCaller)
111 i := ipc.NewIPC()
112 i.Serve(caller)
113 conn, err := i.Connect(path)
114 if err != nil {
115 return nil, err
116 }
117 caller.conn = conn
118 agent := &client{caller: caller}
119 if err := agent.fetchPublicKey(); err != nil {
120 return nil, err
121 }
122 return agent, nil
123}
124
125// NewAgentPrincipal returns a security.Pricipal using the PrivateKey held in a remote agent process.
126// 'path' is the path to the agent socket, typically obtained from
127// os.GetEnv(envvar.AgentAddress).
128func NewAgentPrincipalX(path string) (agent.Principal, error) {
129 p, err := newUncachedPrincipalX(path)
130 if err != nil {
131 return nil, err
132 }
133 cached, flush, err := cache.NewCachedPrincipalX(p)
134 if err != nil {
135 return nil, err
136 }
137 caller := p.caller.(*ipcCaller)
138 caller.mu.Lock()
139 caller.flush = flush
140 caller.mu.Unlock()
141 return cached, nil
142}
143
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700144// NewAgentPrincipal returns a security.Pricipal using the PrivateKey held in a remote agent process.
Ryan Brown7f950a82015-04-20 18:08:39 -0700145// 'endpoint' is the endpoint for connecting to the agent, typically obtained from
146// os.GetEnv(envvar.AgentEndpoint).
Bogdan Caprita6613fc42015-01-28 11:54:23 -0800147// 'ctx' should not have a deadline, and should never be cancelled while the
148// principal is in use.
Ryan Brown7f950a82015-04-20 18:08:39 -0700149func NewAgentPrincipal(ctx *context.T, endpoint naming.Endpoint, insecureClient rpc.Client) (security.Principal, error) {
150 p, err := newUncachedPrincipal(ctx, endpoint, insecureClient)
Ryan Brown81bcb3a2015-02-11 10:58:01 -0800151 if err != nil {
152 return p, err
153 }
Ryan Brown0a3e28a2015-08-12 14:59:14 -0700154 caller := p.caller.(*vrpcCaller)
155 call, callErr := caller.startCall("NotifyWhenChanged")
Ryan Brown81bcb3a2015-02-11 10:58:01 -0800156 if callErr != nil {
157 return nil, callErr
158 }
Ryan Brown0a3e28a2015-08-12 14:59:14 -0700159 return cache.NewCachedPrincipal(caller.ctx, p, call)
Ryan Brown81bcb3a2015-02-11 10:58:01 -0800160}
Ryan Brown7f950a82015-04-20 18:08:39 -0700161func newUncachedPrincipal(ctx *context.T, ep naming.Endpoint, insecureClient rpc.Client) (*client, error) {
162 // This isn't a real vanadium endpoint. It contains the vanadium version
163 // info, but the address is serving the agent protocol.
164 if ep.Addr().Network() != "" {
165 return nil, verror.New(errInvalidProtocol, ctx, ep.Addr().Network())
166 }
167 fd, err := strconv.Atoi(ep.Addr().String())
168 if err != nil {
169 return nil, err
170 }
171 syscall.ForkLock.Lock()
172 fd, err = syscall.Dup(fd)
173 if err == nil {
174 syscall.CloseOnExec(fd)
175 }
176 syscall.ForkLock.Unlock()
177 if err != nil {
178 return nil, err
179 }
Ryan Brown81789442014-10-30 13:23:53 -0700180 f := os.NewFile(uintptr(fd), "agent_client")
181 defer f.Close()
182 conn, err := net.FileConn(f)
Ryan Brown50b473a2014-09-23 14:23:00 -0700183 if err != nil {
184 return nil, err
185 }
186 // This is just an arbitrary 1 byte string. The value is ignored.
187 data := make([]byte, 1)
Bogdan Capritabb37c542015-01-22 10:21:57 -0800188 addr, err := unixfd.SendConnection(conn.(*net.UnixConn), data)
Ryan Brown50b473a2014-09-23 14:23:00 -0700189 if err != nil {
190 return nil, err
191 }
Ryan Brown0a3e28a2015-08-12 14:59:14 -0700192 ctx, cancel := context.WithCancel(ctx)
193 caller := &vrpcCaller{
Matt Rosencrantz99cc06e2015-01-16 10:25:11 -0800194 client: insecureClient,
Ryan Brown7f950a82015-04-20 18:08:39 -0700195 name: naming.JoinAddressName(agentEndpoint("unixfd", addr.String()), ""),
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700196 ctx: ctx,
Ryan Brown0a3e28a2015-08-12 14:59:14 -0700197 cancel: cancel,
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700198 }
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700199 agent := &client{caller: caller}
Ryan Brownfed691e2014-09-15 13:09:40 -0700200 if err := agent.fetchPublicKey(); err != nil {
201 return nil, err
202 }
203 return agent, nil
204}
205
Ryan Brown0a3e28a2015-08-12 14:59:14 -0700206func (c *client) Close() error {
207 return c.caller.Close()
208}
209
Ryan Brownfed691e2014-09-15 13:09:40 -0700210func (c *client) fetchPublicKey() (err error) {
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700211 var b []byte
212 if err = c.caller.call("PublicKey", results(&b)); err != nil {
Ryan Brownfed691e2014-09-15 13:09:40 -0700213 return
214 }
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700215 c.key, err = security.UnmarshalPublicKey(b)
Ryan Brownfed691e2014-09-15 13:09:40 -0700216 return
217}
218
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700219func (c *client) Bless(key security.PublicKey, with security.Blessings, extension string, caveat security.Caveat, additionalCaveats ...security.Caveat) (security.Blessings, error) {
Asim Shankarb07ec692015-02-27 23:40:44 -0800220 var blessings security.Blessings
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700221 marshalledKey, err := key.MarshalBinary()
222 if err != nil {
Asim Shankar2bf7b1e2015-02-27 00:45:12 -0800223 return security.Blessings{}, err
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700224 }
Asim Shankarb07ec692015-02-27 23:40:44 -0800225 err = c.caller.call("Bless", results(&blessings), marshalledKey, with, extension, caveat, additionalCaveats)
226 return blessings, err
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700227}
228
229func (c *client) BlessSelf(name string, caveats ...security.Caveat) (security.Blessings, error) {
Asim Shankarb07ec692015-02-27 23:40:44 -0800230 var blessings security.Blessings
231 err := c.caller.call("BlessSelf", results(&blessings), name, caveats)
232 return blessings, err
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700233}
234
235func (c *client) Sign(message []byte) (sig security.Signature, err error) {
236 err = c.caller.call("Sign", results(&sig), message)
237 return
238}
239
Asim Shankar19da8182015-02-06 01:41:16 -0800240func (c *client) MintDischarge(forCaveat, caveatOnDischarge security.Caveat, additionalCaveatsOnDischarge ...security.Caveat) (security.Discharge, error) {
Asim Shankar08642822015-03-02 21:21:09 -0800241 var discharge security.Discharge
Asim Shankar19da8182015-02-06 01:41:16 -0800242 if err := c.caller.call("MintDischarge", results(&discharge), forCaveat, caveatOnDischarge, additionalCaveatsOnDischarge); err != nil {
Asim Shankar3ad0b8a2015-02-25 00:37:21 -0800243 return security.Discharge{}, err
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700244 }
Asim Shankar08642822015-03-02 21:21:09 -0800245 return discharge, nil
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700246}
247
Ryan Brownfed691e2014-09-15 13:09:40 -0700248func (c *client) PublicKey() security.PublicKey {
249 return c.key
250}
251
gauthamtf8263932014-12-16 10:59:09 -0800252func (c *client) BlessingsByName(pattern security.BlessingPattern) []security.Blessings {
Asim Shankarb07ec692015-02-27 23:40:44 -0800253 var blessings []security.Blessings
254 if err := c.caller.call("BlessingsByName", results(&blessings), pattern); err != nil {
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700255 logger.Global().Infof("error calling BlessingsByName: %v", err)
gauthamtf8263932014-12-16 10:59:09 -0800256 return nil
257 }
gauthamtf8263932014-12-16 10:59:09 -0800258 return blessings
259}
260
gauthamt8dc9a182015-01-08 18:03:18 -0800261func (c *client) BlessingsInfo(blessings security.Blessings) map[string][]security.Caveat {
262 var bInfo map[string][]security.Caveat
Asim Shankarb07ec692015-02-27 23:40:44 -0800263 err := c.caller.call("BlessingsInfo", results(&bInfo), blessings)
gauthamtf8263932014-12-16 10:59:09 -0800264 if err != nil {
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700265 logger.Global().Infof("error calling BlessingsInfo: %v", err)
gauthamtf8263932014-12-16 10:59:09 -0800266 return nil
267 }
gauthamt8dc9a182015-01-08 18:03:18 -0800268 return bInfo
gauthamtf8263932014-12-16 10:59:09 -0800269}
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700270
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700271func (c *client) BlessingStore() security.BlessingStore {
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700272 return &blessingStore{caller: c.caller, key: c.key}
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700273}
274
275func (c *client) Roots() security.BlessingRoots {
276 return &blessingRoots{c.caller}
277}
278
279func (c *client) AddToRoots(blessings security.Blessings) error {
Asim Shankarb07ec692015-02-27 23:40:44 -0800280 return c.caller.call("AddToRoots", results(), blessings)
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700281}
282
283type blessingStore struct {
284 caller caller
285 key security.PublicKey
286}
287
288func (b *blessingStore) Set(blessings security.Blessings, forPeers security.BlessingPattern) (security.Blessings, error) {
Asim Shankarb07ec692015-02-27 23:40:44 -0800289 var previous security.Blessings
290 err := b.caller.call("BlessingStoreSet", results(&previous), blessings, forPeers)
291 return previous, err
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700292}
293
294func (b *blessingStore) ForPeer(peerBlessings ...string) security.Blessings {
Asim Shankarb07ec692015-02-27 23:40:44 -0800295 var blessings security.Blessings
296 if err := b.caller.call("BlessingStoreForPeer", results(&blessings), peerBlessings); err != nil {
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700297 logger.Global().Infof("error calling BlessingStorePeerBlessings: %v", err)
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700298 }
299 return blessings
300}
301
302func (b *blessingStore) SetDefault(blessings security.Blessings) error {
Asim Shankarb07ec692015-02-27 23:40:44 -0800303 return b.caller.call("BlessingStoreSetDefault", results(), blessings)
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700304}
305
306func (b *blessingStore) Default() security.Blessings {
Asim Shankarb07ec692015-02-27 23:40:44 -0800307 var blessings security.Blessings
308 err := b.caller.call("BlessingStoreDefault", results(&blessings))
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700309 if err != nil {
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700310 logger.Global().Infof("error calling BlessingStoreDefault: %v", err)
Asim Shankar2bf7b1e2015-02-27 00:45:12 -0800311 return security.Blessings{}
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700312 }
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700313 return blessings
314}
315
316func (b *blessingStore) PublicKey() security.PublicKey {
317 return b.key
318}
319
gauthamtf8263932014-12-16 10:59:09 -0800320func (b *blessingStore) PeerBlessings() map[security.BlessingPattern]security.Blessings {
Asim Shankarb07ec692015-02-27 23:40:44 -0800321 var bmap map[security.BlessingPattern]security.Blessings
322 err := b.caller.call("BlessingStorePeerBlessings", results(&bmap))
gauthamtf8263932014-12-16 10:59:09 -0800323 if err != nil {
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700324 logger.Global().Infof("error calling BlessingStorePeerBlessings: %v", err)
gauthamtf8263932014-12-16 10:59:09 -0800325 return nil
326 }
Asim Shankarb07ec692015-02-27 23:40:44 -0800327 return bmap
gauthamtf8263932014-12-16 10:59:09 -0800328}
329
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700330func (b *blessingStore) DebugString() (s string) {
331 err := b.caller.call("BlessingStoreDebugString", results(&s))
332 if err != nil {
333 s = fmt.Sprintf("error calling BlessingStoreDebugString: %v", err)
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700334 logger.Global().Infof(s)
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700335 }
336 return
337}
338
Suharsh Sivakumard7d4e222015-06-22 11:10:44 -0700339func (b *blessingStore) CacheDischarge(d security.Discharge, c security.Caveat, i security.DischargeImpetus) {
340 err := b.caller.call("BlessingStoreCacheDischarge", results(), d, c, i)
341 if err != nil {
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700342 logger.Global().Infof("error calling BlessingStoreCacheDischarge: %v", err)
Suharsh Sivakumard7d4e222015-06-22 11:10:44 -0700343 }
344}
345
346func (b *blessingStore) ClearDischarges(discharges ...security.Discharge) {
347 err := b.caller.call("BlessingStoreClearDischarges", results(), discharges)
348 if err != nil {
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700349 logger.Global().Infof("error calling BlessingStoreClearDischarges: %v", err)
Suharsh Sivakumard7d4e222015-06-22 11:10:44 -0700350 }
351}
352
353func (b *blessingStore) Discharge(caveat security.Caveat, impetus security.DischargeImpetus) (out security.Discharge) {
354 err := b.caller.call("BlessingStoreDischarge", results(&out), caveat, impetus)
355 if err != nil {
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700356 logger.Global().Infof("error calling BlessingStoreDischarge: %v", err)
Suharsh Sivakumard7d4e222015-06-22 11:10:44 -0700357 }
358 return
359}
360
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700361type blessingRoots struct {
362 caller caller
363}
364
Asim Shankar6eba9862015-09-03 01:07:14 -0400365func (b *blessingRoots) Add(root []byte, pattern security.BlessingPattern) error {
366 return b.caller.call("BlessingRootsAdd", results(), root, pattern)
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700367}
368
Asim Shankar6eba9862015-09-03 01:07:14 -0400369func (b *blessingRoots) Recognized(root []byte, blessing string) error {
370 return b.caller.call("BlessingRootsRecognized", results(), root, blessing)
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700371}
372
Ankur9e5b7722015-04-28 15:00:25 -0700373func (b *blessingRoots) Dump() map[security.BlessingPattern][]security.PublicKey {
374 var marshaledRoots map[security.BlessingPattern][][]byte
375 if err := b.caller.call("BlessingRootsDump", results(&marshaledRoots)); err != nil {
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700376 logger.Global().Infof("error calling BlessingRootsDump: %v", err)
Ankur9e5b7722015-04-28 15:00:25 -0700377 return nil
378 }
379 ret := make(map[security.BlessingPattern][]security.PublicKey)
380 for p, marshaledKeys := range marshaledRoots {
381 for _, marshaledKey := range marshaledKeys {
382 key, err := security.UnmarshalPublicKey(marshaledKey)
383 if err != nil {
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700384 logger.Global().Infof("security.UnmarshalPublicKey(%v) returned error: %v", marshaledKey, err)
Ankur9e5b7722015-04-28 15:00:25 -0700385 continue
386 }
387 ret[p] = append(ret[p], key)
388 }
389 }
390 return ret
391}
392
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700393func (b *blessingRoots) DebugString() (s string) {
394 err := b.caller.call("BlessingRootsDebugString", results(&s))
395 if err != nil {
396 s = fmt.Sprintf("error calling BlessingRootsDebugString: %v", err)
Cosmos Nicolaou1c33b7d2015-06-24 15:15:54 -0700397 logger.Global().Infof(s)
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700398 }
Ryan Brownfed691e2014-09-15 13:09:40 -0700399 return
400}
Ryan Brown7f950a82015-04-20 18:08:39 -0700401
402func agentEndpoint(proto, addr string) string {
Suharsh Sivakumarfa2e50a2015-08-31 12:35:12 -0700403 // TODO: use naming.FormatEndpoint when it supports version 6.
404 return fmt.Sprintf("@6@%s@%s@@@s@@@", proto, addr)
Ryan Brown7f950a82015-04-20 18:08:39 -0700405}
406
407func AgentEndpoint(fd int) string {
408 // We use an empty protocol here because this isn't really speaking
409 // veyron rpc.
410 return agentEndpoint("", fmt.Sprintf("%d", fd))
411}