blob: e540707cca78f2cf3f455a490c1908333bf50e37 [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
Matt Rosencrantzbca49812015-03-01 21:32:54 -08005// The following enables go generate to generate the doc.go file.
Jiri Simsa32f76fb2015-04-07 15:39:23 -07006//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go .
Matt Rosencrantzbca49812015-03-01 21:32:54 -08007
8package main
9
10import (
11 "fmt"
12 "io"
13 "os"
Todd Wang1624bf92015-04-22 16:53:57 -070014 "regexp"
Matt Rosencrantzbca49812015-03-01 21:32:54 -080015 "strings"
16 "time"
17
18 "v.io/v23"
19 "v.io/v23/context"
Cosmos Nicolaou28343a22015-04-15 11:01:09 -070020 "v.io/v23/naming"
Matt Rosencrantzbca49812015-03-01 21:32:54 -080021 "v.io/v23/options"
Asim Shankar1c448c52015-04-01 17:02:38 -070022 "v.io/v23/rpc"
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070023 "v.io/v23/rpc/reserved"
Matt Rosencrantzbca49812015-03-01 21:32:54 -080024 "v.io/v23/vdl"
25 "v.io/v23/vdlroot/signature"
26 "v.io/x/lib/cmdline"
27 "v.io/x/ref/lib/vdl/build"
28 "v.io/x/ref/lib/vdl/codegen/vdlgen"
29 "v.io/x/ref/lib/vdl/compile"
30
31 _ "v.io/x/ref/profiles"
32)
33
Asim Shankar1c448c52015-04-01 17:02:38 -070034var (
Cosmos Nicolaou28343a22015-04-15 11:01:09 -070035 gctx *context.T
36 flagInsecure bool
37 flagShowReserved bool
Asim Shankar1c448c52015-04-01 17:02:38 -070038)
Matt Rosencrantzbca49812015-03-01 21:32:54 -080039
40func main() {
Todd Wang1624bf92015-04-22 16:53:57 -070041 cmdline.HideGlobalFlagsExcept(regexp.MustCompile(`^v23\.namespace\.root$`))
Matt Rosencrantzbca49812015-03-01 21:32:54 -080042 var shutdown v23.Shutdown
43 gctx, shutdown = v23.Init()
44 exitCode := cmdVRPC.Main()
45 shutdown()
46 os.Exit(exitCode)
47}
48
Asim Shankar1c448c52015-04-01 17:02:38 -070049func init() {
50 const (
51 insecureVal = false
52 insecureName = "insecure"
53 insecureDesc = "If true, skip server authentication. This means that the client will reveal its blessings to servers that it may not recognize."
54 )
55 cmdSignature.Flags.BoolVar(&flagInsecure, insecureName, insecureVal, insecureDesc)
56 cmdIdentify.Flags.BoolVar(&flagInsecure, insecureName, insecureVal, insecureDesc)
Cosmos Nicolaou28343a22015-04-15 11:01:09 -070057
58 cmdSignature.Flags.BoolVar(&flagShowReserved, "show-reserved", false, "if true, also show the signatures of reserved methods")
Asim Shankar1c448c52015-04-01 17:02:38 -070059}
60
Matt Rosencrantzbca49812015-03-01 21:32:54 -080061var cmdVRPC = &cmdline.Command{
62 Name: "vrpc",
Todd Wang6ed3b6c2015-04-08 14:37:04 -070063 Short: "sends and receives Vanadium remote procedure calls",
Matt Rosencrantzbca49812015-03-01 21:32:54 -080064 Long: `
Todd Wang6ed3b6c2015-04-08 14:37:04 -070065Command vrpc sends and receives Vanadium remote procedure calls. It is used as
66a generic client to interact with any Vanadium server.
Matt Rosencrantzbca49812015-03-01 21:32:54 -080067`,
68 // TODO(toddw): Add cmdServe, which will take an interface as input, and set
69 // up a server capable of handling the given methods. When a request is
70 // received, it'll allow the user to respond via stdin.
71 Children: []*cmdline.Command{cmdSignature, cmdCall, cmdIdentify},
72}
73
74const serverDesc = `
75<server> identifies a Vanadium server. It can either be the object address of
76the server, or an object name that will be resolved to an end-point.
77`
78
79var cmdSignature = &cmdline.Command{
80 Run: runSignature,
81 Name: "signature",
82 Short: "Describe the interfaces of a Vanadium server",
83 Long: `
84Signature connects to the Vanadium server identified by <server>.
85
86If no [method] is provided, returns all interfaces implemented by the server.
87
88If a [method] is provided, returns the signature of just that method.
89`,
90 ArgsName: "<server> [method]",
91 ArgsLong: serverDesc + `
92[method] is the optional server method name.
93`,
94}
95
96var cmdCall = &cmdline.Command{
97 Run: runCall,
98 Name: "call",
99 Short: "Call a method of a Vanadium server",
100 Long: `
101Call connects to the Vanadium server identified by <server> and calls the
102<method> with the given positional [args...], returning results on stdout.
103
104TODO(toddw): stdin is read for streaming arguments sent to the server. An EOF
105on stdin (e.g. via ^D) causes the send stream to be closed.
106
107Regardless of whether the call is streaming, the main goroutine blocks for
108streaming and positional results received from the server.
109
110All input arguments (both positional and streaming) are specified as VDL
111expressions, with commas separating multiple expressions. Positional arguments
112may also be specified as separate command-line arguments. Streaming arguments
113may also be specified as separate newline-terminated expressions.
114
115The method signature is always retrieved from the server as a first step. This
116makes it easier to input complex typed arguments, since the top-level type for
117each argument is implicit and doesn't need to be specified.
118`,
119 ArgsName: "<server> <method> [args...]",
120 ArgsLong: serverDesc + `
121<method> is the server method to call.
122
123[args...] are the positional input arguments, specified as VDL expressions.
124`,
125}
126
127var cmdIdentify = &cmdline.Command{
128 Run: runIdentify,
129 Name: "identify",
130 Short: "Reveal blessings presented by a Vanadium server",
131 Long: `
132Identify connects to the Vanadium server identified by <server> and dumps out
133the blessings presented by that server (and the subset of those that are
134considered valid by the principal running this tool) to standard output.
135`,
136 ArgsName: "<server>",
137 ArgsLong: serverDesc,
138}
139
140func runSignature(cmd *cmdline.Command, args []string) error {
141 // Error-check args.
142 var server, method string
143 switch len(args) {
144 case 1:
145 server = args[0]
146 case 2:
147 server, method = args[0], args[1]
148 default:
149 return cmd.UsageErrorf("wrong number of arguments")
150 }
151 // Get the interface or method signature, and pretty-print. We print the
152 // named types after the signatures, to aid in readability.
153 ctx, cancel := context.WithTimeout(gctx, time.Minute)
154 defer cancel()
155 var types vdlgen.NamedTypes
Asim Shankar1c448c52015-04-01 17:02:38 -0700156 var opts []rpc.CallOpt
157 if flagInsecure {
158 opts = append(opts, options.SkipServerEndpointAuthorization{})
159 }
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800160 if method != "" {
Asim Shankar1c448c52015-04-01 17:02:38 -0700161 methodSig, err := reserved.MethodSignature(ctx, server, method, opts...)
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800162 if err != nil {
163 return fmt.Errorf("MethodSignature failed: %v", err)
164 }
165 vdlgen.PrintMethod(cmd.Stdout(), methodSig, &types)
166 fmt.Fprintln(cmd.Stdout())
167 types.Print(cmd.Stdout())
168 return nil
169 }
Asim Shankar1c448c52015-04-01 17:02:38 -0700170 ifacesSig, err := reserved.Signature(ctx, server, opts...)
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800171 if err != nil {
172 return fmt.Errorf("Signature failed: %v", err)
173 }
174 for i, iface := range ifacesSig {
Cosmos Nicolaou28343a22015-04-15 11:01:09 -0700175 if !flagShowReserved && naming.IsReserved(iface.Name) {
176 continue
177 }
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800178 if i > 0 {
179 fmt.Fprintln(cmd.Stdout())
180 }
181 vdlgen.PrintInterface(cmd.Stdout(), iface, &types)
182 fmt.Fprintln(cmd.Stdout())
183 }
184 types.Print(cmd.Stdout())
185 return nil
186}
187
188func runCall(cmd *cmdline.Command, args []string) error {
189 // Error-check args, and set up argsdata with a comma-separated list of
190 // arguments, allowing each individual arg to already be comma-separated.
191 //
192 // TODO(toddw): Should we just space-separate the args instead?
193 if len(args) < 2 {
194 return cmd.UsageErrorf("must specify <server> and <method>")
195 }
196 server, method := args[0], args[1]
197 var argsdata string
198 for _, arg := range args[2:] {
199 arg := strings.TrimSpace(arg)
200 if argsdata == "" || strings.HasSuffix(argsdata, ",") || strings.HasPrefix(arg, ",") {
201 argsdata += arg
202 } else {
203 argsdata += "," + arg
204 }
205 }
206 // Get the method signature and parse args.
207 ctx, cancel := context.WithTimeout(gctx, time.Minute)
208 defer cancel()
209 methodSig, err := reserved.MethodSignature(ctx, server, method)
210 if err != nil {
211 return fmt.Errorf("MethodSignature failed: %v", err)
212 }
213 inargs, err := parseInArgs(argsdata, methodSig)
214 if err != nil {
215 // TODO: Print signature and example.
216 return err
217 }
218 // Start the method call.
219 call, err := v23.GetClient(ctx).StartCall(ctx, server, method, inargs)
220 if err != nil {
221 return fmt.Errorf("StartCall failed: %v", err)
222 }
223 // TODO(toddw): Fire off a goroutine to handle streaming inputs.
224 // Handle streaming results.
225StreamingResultsLoop:
226 for {
227 var item *vdl.Value
228 switch err := call.Recv(&item); {
229 case err == io.EOF:
230 break StreamingResultsLoop
231 case err != nil:
232 return fmt.Errorf("call.Recv failed: %v", err)
233 }
234 fmt.Fprintf(cmd.Stdout(), "<< %v\n", vdlgen.TypedConst(item, "", nil))
235 }
236 // Finish the method call.
237 outargs := make([]*vdl.Value, len(methodSig.OutArgs))
238 outptrs := make([]interface{}, len(outargs))
239 for i := range outargs {
240 outptrs[i] = &outargs[i]
241 }
242 if err := call.Finish(outptrs...); err != nil {
243 return fmt.Errorf("call.Finish failed: %v", err)
244 }
245 // Pretty-print results.
246 for i, arg := range outargs {
247 if i > 0 {
248 fmt.Fprint(cmd.Stdout(), " ")
249 }
250 fmt.Fprint(cmd.Stdout(), vdlgen.TypedConst(arg, "", nil))
251 }
252 fmt.Fprintln(cmd.Stdout())
253 return nil
254}
255
256func parseInArgs(argsdata string, methodSig signature.Method) ([]interface{}, error) {
257 if len(methodSig.InArgs) == 0 {
258 return nil, nil
259 }
260 var intypes []*vdl.Type
261 for _, inarg := range methodSig.InArgs {
262 intypes = append(intypes, inarg.Type)
263 }
264 env := compile.NewEnv(-1)
265 inargs := build.BuildExprs(argsdata, intypes, env)
266 if err := env.Errors.ToError(); err != nil {
267 return nil, fmt.Errorf("can't parse in-args:\n%v", err)
268 }
269 if got, want := len(inargs), len(methodSig.InArgs); got != want {
270 return nil, fmt.Errorf("got %d args, want %d", got, want)
271 }
272 // Translate []*vdl.Value to []interface, with each item still *vdl.Value.
273 var ret []interface{}
274 for _, arg := range inargs {
275 ret = append(ret, arg)
276 }
277 return ret, nil
278}
279
280func runIdentify(cmd *cmdline.Command, args []string) error {
281 if len(args) != 1 {
282 return cmd.UsageErrorf("wrong number of arguments")
283 }
284 server := args[0]
285 ctx, cancel := context.WithTimeout(gctx, time.Minute)
286 defer cancel()
Asim Shankar1c448c52015-04-01 17:02:38 -0700287 var opts []rpc.CallOpt
288 if flagInsecure {
289 opts = append(opts, options.SkipServerEndpointAuthorization{})
290 }
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800291 // The method name does not matter - only interested in authentication,
292 // not in actually making an RPC.
Asim Shankar1c448c52015-04-01 17:02:38 -0700293 call, err := v23.GetClient(ctx).StartCall(ctx, server, "", nil, opts...)
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800294 if err != nil {
295 return fmt.Errorf(`client.StartCall(%q, "", nil) failed with %v`, server, err)
296 }
297 valid, presented := call.RemoteBlessings()
Asim Shankar1c448c52015-04-01 17:02:38 -0700298 fmt.Fprintf(cmd.Stdout(), "PRESENTED: %v\nVALID: %v\nPUBLICKEY: %v\n", presented, valid, presented.PublicKey())
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800299 return nil
300}