blob: e0f35e0e4f94ebdb87a05b28f9d094fe698d24ee [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"
Todd Wang1624bf92015-04-22 16:53:57 -070013 "regexp"
Matt Rosencrantzbca49812015-03-01 21:32:54 -080014 "strings"
15 "time"
16
17 "v.io/v23"
18 "v.io/v23/context"
Cosmos Nicolaou28343a22015-04-15 11:01:09 -070019 "v.io/v23/naming"
Matt Rosencrantzbca49812015-03-01 21:32:54 -080020 "v.io/v23/options"
Asim Shankar1c448c52015-04-01 17:02:38 -070021 "v.io/v23/rpc"
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070022 "v.io/v23/rpc/reserved"
Matt Rosencrantzbca49812015-03-01 21:32:54 -080023 "v.io/v23/vdl"
24 "v.io/v23/vdlroot/signature"
Todd Wang9560b9c2015-05-11 13:27:58 -070025 "v.io/x/lib/cmdline"
Todd Wang338f90c2015-05-07 19:42:46 -070026 "v.io/x/ref/lib/v23cmd"
Matt Rosencrantzbca49812015-03-01 21:32:54 -080027 "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
Suharsh Sivakumardcc11d72015-05-11 12:19:20 -070031 _ "v.io/x/ref/runtime/factories/generic"
Matt Rosencrantzbca49812015-03-01 21:32:54 -080032)
33
Asim Shankar1c448c52015-04-01 17:02:38 -070034var (
Cosmos Nicolaou28343a22015-04-15 11:01:09 -070035 flagInsecure bool
36 flagShowReserved bool
Asim Shankar1c448c52015-04-01 17:02:38 -070037)
Matt Rosencrantzbca49812015-03-01 21:32:54 -080038
39func main() {
Todd Wang9560b9c2015-05-11 13:27:58 -070040 cmdline.HideGlobalFlagsExcept(regexp.MustCompile(`^v23\.namespace\.root$`))
41 cmdline.Main(cmdVRPC)
Matt Rosencrantzbca49812015-03-01 21:32:54 -080042}
43
Asim Shankar1c448c52015-04-01 17:02:38 -070044func init() {
45 const (
46 insecureVal = false
47 insecureName = "insecure"
48 insecureDesc = "If true, skip server authentication. This means that the client will reveal its blessings to servers that it may not recognize."
49 )
50 cmdSignature.Flags.BoolVar(&flagInsecure, insecureName, insecureVal, insecureDesc)
51 cmdIdentify.Flags.BoolVar(&flagInsecure, insecureName, insecureVal, insecureDesc)
Cosmos Nicolaou28343a22015-04-15 11:01:09 -070052
53 cmdSignature.Flags.BoolVar(&flagShowReserved, "show-reserved", false, "if true, also show the signatures of reserved methods")
Asim Shankar1c448c52015-04-01 17:02:38 -070054}
55
Todd Wang9560b9c2015-05-11 13:27:58 -070056var cmdVRPC = &cmdline.Command{
Matt Rosencrantzbca49812015-03-01 21:32:54 -080057 Name: "vrpc",
Todd Wang6ed3b6c2015-04-08 14:37:04 -070058 Short: "sends and receives Vanadium remote procedure calls",
Matt Rosencrantzbca49812015-03-01 21:32:54 -080059 Long: `
Todd Wang6ed3b6c2015-04-08 14:37:04 -070060Command vrpc sends and receives Vanadium remote procedure calls. It is used as
61a generic client to interact with any Vanadium server.
Matt Rosencrantzbca49812015-03-01 21:32:54 -080062`,
63 // TODO(toddw): Add cmdServe, which will take an interface as input, and set
64 // up a server capable of handling the given methods. When a request is
65 // received, it'll allow the user to respond via stdin.
Todd Wang9560b9c2015-05-11 13:27:58 -070066 Children: []*cmdline.Command{cmdSignature, cmdCall, cmdIdentify},
Matt Rosencrantzbca49812015-03-01 21:32:54 -080067}
68
69const serverDesc = `
70<server> identifies a Vanadium server. It can either be the object address of
71the server, or an object name that will be resolved to an end-point.
72`
73
Todd Wang9560b9c2015-05-11 13:27:58 -070074var cmdSignature = &cmdline.Command{
Todd Wang338f90c2015-05-07 19:42:46 -070075 Runner: v23cmd.RunnerFunc(runSignature),
76 Name: "signature",
77 Short: "Describe the interfaces of a Vanadium server",
Matt Rosencrantzbca49812015-03-01 21:32:54 -080078 Long: `
79Signature connects to the Vanadium server identified by <server>.
80
81If no [method] is provided, returns all interfaces implemented by the server.
82
83If a [method] is provided, returns the signature of just that method.
84`,
85 ArgsName: "<server> [method]",
86 ArgsLong: serverDesc + `
87[method] is the optional server method name.
88`,
89}
90
Todd Wang9560b9c2015-05-11 13:27:58 -070091var cmdCall = &cmdline.Command{
Todd Wang338f90c2015-05-07 19:42:46 -070092 Runner: v23cmd.RunnerFunc(runCall),
93 Name: "call",
94 Short: "Call a method of a Vanadium server",
Matt Rosencrantzbca49812015-03-01 21:32:54 -080095 Long: `
96Call connects to the Vanadium server identified by <server> and calls the
97<method> with the given positional [args...], returning results on stdout.
98
99TODO(toddw): stdin is read for streaming arguments sent to the server. An EOF
100on stdin (e.g. via ^D) causes the send stream to be closed.
101
102Regardless of whether the call is streaming, the main goroutine blocks for
103streaming and positional results received from the server.
104
105All input arguments (both positional and streaming) are specified as VDL
106expressions, with commas separating multiple expressions. Positional arguments
107may also be specified as separate command-line arguments. Streaming arguments
108may also be specified as separate newline-terminated expressions.
109
110The method signature is always retrieved from the server as a first step. This
111makes it easier to input complex typed arguments, since the top-level type for
112each argument is implicit and doesn't need to be specified.
113`,
114 ArgsName: "<server> <method> [args...]",
115 ArgsLong: serverDesc + `
116<method> is the server method to call.
117
118[args...] are the positional input arguments, specified as VDL expressions.
119`,
120}
121
Todd Wang9560b9c2015-05-11 13:27:58 -0700122var cmdIdentify = &cmdline.Command{
Todd Wang338f90c2015-05-07 19:42:46 -0700123 Runner: v23cmd.RunnerFunc(runIdentify),
124 Name: "identify",
125 Short: "Reveal blessings presented by a Vanadium server",
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800126 Long: `
127Identify connects to the Vanadium server identified by <server> and dumps out
128the blessings presented by that server (and the subset of those that are
129considered valid by the principal running this tool) to standard output.
130`,
131 ArgsName: "<server>",
132 ArgsLong: serverDesc,
133}
134
Todd Wang9560b9c2015-05-11 13:27:58 -0700135func runSignature(ctx *context.T, env *cmdline.Env, args []string) error {
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800136 // Error-check args.
137 var server, method string
138 switch len(args) {
139 case 1:
140 server = args[0]
141 case 2:
142 server, method = args[0], args[1]
143 default:
Todd Wang338f90c2015-05-07 19:42:46 -0700144 return env.UsageErrorf("wrong number of arguments")
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800145 }
146 // Get the interface or method signature, and pretty-print. We print the
147 // named types after the signatures, to aid in readability.
Todd Wang338f90c2015-05-07 19:42:46 -0700148 ctx, cancel := context.WithTimeout(ctx, time.Minute)
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800149 defer cancel()
150 var types vdlgen.NamedTypes
Asim Shankar1c448c52015-04-01 17:02:38 -0700151 var opts []rpc.CallOpt
152 if flagInsecure {
153 opts = append(opts, options.SkipServerEndpointAuthorization{})
154 }
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800155 if method != "" {
Asim Shankar1c448c52015-04-01 17:02:38 -0700156 methodSig, err := reserved.MethodSignature(ctx, server, method, opts...)
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800157 if err != nil {
158 return fmt.Errorf("MethodSignature failed: %v", err)
159 }
Todd Wang338f90c2015-05-07 19:42:46 -0700160 vdlgen.PrintMethod(env.Stdout, methodSig, &types)
161 fmt.Fprintln(env.Stdout)
162 types.Print(env.Stdout)
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800163 return nil
164 }
Asim Shankar1c448c52015-04-01 17:02:38 -0700165 ifacesSig, err := reserved.Signature(ctx, server, opts...)
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800166 if err != nil {
167 return fmt.Errorf("Signature failed: %v", err)
168 }
169 for i, iface := range ifacesSig {
Cosmos Nicolaou28343a22015-04-15 11:01:09 -0700170 if !flagShowReserved && naming.IsReserved(iface.Name) {
171 continue
172 }
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800173 if i > 0 {
Todd Wang338f90c2015-05-07 19:42:46 -0700174 fmt.Fprintln(env.Stdout)
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800175 }
Todd Wang338f90c2015-05-07 19:42:46 -0700176 vdlgen.PrintInterface(env.Stdout, iface, &types)
177 fmt.Fprintln(env.Stdout)
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800178 }
Todd Wang338f90c2015-05-07 19:42:46 -0700179 types.Print(env.Stdout)
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800180 return nil
181}
182
Todd Wang9560b9c2015-05-11 13:27:58 -0700183func runCall(ctx *context.T, env *cmdline.Env, args []string) error {
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800184 // Error-check args, and set up argsdata with a comma-separated list of
185 // arguments, allowing each individual arg to already be comma-separated.
186 //
187 // TODO(toddw): Should we just space-separate the args instead?
188 if len(args) < 2 {
Todd Wang338f90c2015-05-07 19:42:46 -0700189 return env.UsageErrorf("must specify <server> and <method>")
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800190 }
191 server, method := args[0], args[1]
192 var argsdata string
193 for _, arg := range args[2:] {
194 arg := strings.TrimSpace(arg)
195 if argsdata == "" || strings.HasSuffix(argsdata, ",") || strings.HasPrefix(arg, ",") {
196 argsdata += arg
197 } else {
198 argsdata += "," + arg
199 }
200 }
201 // Get the method signature and parse args.
Todd Wang338f90c2015-05-07 19:42:46 -0700202 ctx, cancel := context.WithTimeout(ctx, time.Minute)
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800203 defer cancel()
204 methodSig, err := reserved.MethodSignature(ctx, server, method)
205 if err != nil {
206 return fmt.Errorf("MethodSignature failed: %v", err)
207 }
208 inargs, err := parseInArgs(argsdata, methodSig)
209 if err != nil {
210 // TODO: Print signature and example.
211 return err
212 }
213 // Start the method call.
214 call, err := v23.GetClient(ctx).StartCall(ctx, server, method, inargs)
215 if err != nil {
216 return fmt.Errorf("StartCall failed: %v", err)
217 }
218 // TODO(toddw): Fire off a goroutine to handle streaming inputs.
219 // Handle streaming results.
220StreamingResultsLoop:
221 for {
222 var item *vdl.Value
223 switch err := call.Recv(&item); {
224 case err == io.EOF:
225 break StreamingResultsLoop
226 case err != nil:
227 return fmt.Errorf("call.Recv failed: %v", err)
228 }
Todd Wang338f90c2015-05-07 19:42:46 -0700229 fmt.Fprintf(env.Stdout, "<< %v\n", vdlgen.TypedConst(item, "", nil))
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800230 }
231 // Finish the method call.
232 outargs := make([]*vdl.Value, len(methodSig.OutArgs))
233 outptrs := make([]interface{}, len(outargs))
234 for i := range outargs {
235 outptrs[i] = &outargs[i]
236 }
237 if err := call.Finish(outptrs...); err != nil {
238 return fmt.Errorf("call.Finish failed: %v", err)
239 }
240 // Pretty-print results.
241 for i, arg := range outargs {
242 if i > 0 {
Todd Wang338f90c2015-05-07 19:42:46 -0700243 fmt.Fprint(env.Stdout, " ")
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800244 }
Todd Wang338f90c2015-05-07 19:42:46 -0700245 fmt.Fprint(env.Stdout, vdlgen.TypedConst(arg, "", nil))
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800246 }
Todd Wang338f90c2015-05-07 19:42:46 -0700247 fmt.Fprintln(env.Stdout)
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800248 return nil
249}
250
251func parseInArgs(argsdata string, methodSig signature.Method) ([]interface{}, error) {
252 if len(methodSig.InArgs) == 0 {
253 return nil, nil
254 }
255 var intypes []*vdl.Type
256 for _, inarg := range methodSig.InArgs {
257 intypes = append(intypes, inarg.Type)
258 }
259 env := compile.NewEnv(-1)
260 inargs := build.BuildExprs(argsdata, intypes, env)
261 if err := env.Errors.ToError(); err != nil {
262 return nil, fmt.Errorf("can't parse in-args:\n%v", err)
263 }
264 if got, want := len(inargs), len(methodSig.InArgs); got != want {
265 return nil, fmt.Errorf("got %d args, want %d", got, want)
266 }
267 // Translate []*vdl.Value to []interface, with each item still *vdl.Value.
268 var ret []interface{}
269 for _, arg := range inargs {
270 ret = append(ret, arg)
271 }
272 return ret, nil
273}
274
Todd Wang9560b9c2015-05-11 13:27:58 -0700275func runIdentify(ctx *context.T, env *cmdline.Env, args []string) error {
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800276 if len(args) != 1 {
Todd Wang338f90c2015-05-07 19:42:46 -0700277 return env.UsageErrorf("wrong number of arguments")
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800278 }
279 server := args[0]
Todd Wang338f90c2015-05-07 19:42:46 -0700280 ctx, cancel := context.WithTimeout(ctx, time.Minute)
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800281 defer cancel()
Asim Shankar1c448c52015-04-01 17:02:38 -0700282 var opts []rpc.CallOpt
283 if flagInsecure {
284 opts = append(opts, options.SkipServerEndpointAuthorization{})
285 }
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800286 // The method name does not matter - only interested in authentication,
287 // not in actually making an RPC.
Asim Shankar1c448c52015-04-01 17:02:38 -0700288 call, err := v23.GetClient(ctx).StartCall(ctx, server, "", nil, opts...)
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800289 if err != nil {
290 return fmt.Errorf(`client.StartCall(%q, "", nil) failed with %v`, server, err)
291 }
292 valid, presented := call.RemoteBlessings()
Todd Wang338f90c2015-05-07 19:42:46 -0700293 fmt.Fprintf(env.Stdout, "PRESENTED: %v\nVALID: %v\nPUBLICKEY: %v\n", presented, valid, presented.PublicKey())
Matt Rosencrantzbca49812015-03-01 21:32:54 -0800294 return nil
295}