1/* 2Copyright (c) 2014-2016 VMware, Inc. All Rights Reserved. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package cli 18 19import ( 20 "context" 21 "flag" 22 "fmt" 23 "io" 24 "io/ioutil" 25 "os" 26 "sort" 27 "text/tabwriter" 28 29 "github.com/vmware/govmomi/vim25/types" 30) 31 32type HasFlags interface { 33 // Register may be called more than once and should be idempotent. 34 Register(ctx context.Context, f *flag.FlagSet) 35 36 // Process may be called more than once and should be idempotent. 37 Process(ctx context.Context) error 38} 39 40type Command interface { 41 HasFlags 42 43 Run(ctx context.Context, f *flag.FlagSet) error 44} 45 46func generalHelp(w io.Writer) { 47 fmt.Fprintf(w, "Usage of %s:\n", os.Args[0]) 48 49 cmds := []string{} 50 for name := range commands { 51 cmds = append(cmds, name) 52 } 53 54 sort.Strings(cmds) 55 56 for _, name := range cmds { 57 fmt.Fprintf(w, " %s\n", name) 58 } 59} 60 61func commandHelp(w io.Writer, name string, cmd Command, f *flag.FlagSet) { 62 type HasUsage interface { 63 Usage() string 64 } 65 66 fmt.Fprintf(w, "Usage: %s %s [OPTIONS]", os.Args[0], name) 67 if u, ok := cmd.(HasUsage); ok { 68 fmt.Fprintf(w, " %s", u.Usage()) 69 } 70 fmt.Fprintf(w, "\n") 71 72 type HasDescription interface { 73 Description() string 74 } 75 76 if u, ok := cmd.(HasDescription); ok { 77 fmt.Fprintf(w, "\n%s\n", u.Description()) 78 } 79 80 n := 0 81 f.VisitAll(func(_ *flag.Flag) { 82 n += 1 83 }) 84 85 if n > 0 { 86 fmt.Fprintf(w, "\nOptions:\n") 87 tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0) 88 f.VisitAll(func(f *flag.Flag) { 89 fmt.Fprintf(tw, "\t-%s=%s\t%s\n", f.Name, f.DefValue, f.Usage) 90 }) 91 tw.Flush() 92 } 93} 94 95func clientLogout(ctx context.Context, cmd Command) error { 96 type logout interface { 97 Logout(context.Context) error 98 } 99 100 if l, ok := cmd.(logout); ok { 101 return l.Logout(ctx) 102 } 103 104 return nil 105} 106 107func Run(args []string) int { 108 hw := os.Stderr 109 rc := 1 110 hwrc := func(arg string) { 111 if arg == "-h" { 112 hw = os.Stdout 113 rc = 0 114 } 115 } 116 117 var err error 118 119 if len(args) == 0 { 120 generalHelp(hw) 121 return rc 122 } 123 124 // Look up real command name in aliases table. 125 name, ok := aliases[args[0]] 126 if !ok { 127 name = args[0] 128 } 129 130 cmd, ok := commands[name] 131 if !ok { 132 hwrc(name) 133 generalHelp(hw) 134 return rc 135 } 136 137 fs := flag.NewFlagSet("", flag.ContinueOnError) 138 fs.SetOutput(ioutil.Discard) 139 140 ctx := context.Background() 141 142 if id := os.Getenv("GOVC_OPERATION_ID"); id != "" { 143 ctx = context.WithValue(ctx, types.ID{}, id) 144 } 145 146 cmd.Register(ctx, fs) 147 148 if err = fs.Parse(args[1:]); err != nil { 149 goto error 150 } 151 152 if err = cmd.Process(ctx); err != nil { 153 goto error 154 } 155 156 if err = cmd.Run(ctx, fs); err != nil { 157 goto error 158 } 159 160 if err = clientLogout(ctx, cmd); err != nil { 161 goto error 162 } 163 164 return 0 165 166error: 167 if err == flag.ErrHelp { 168 if len(args) == 2 { 169 hwrc(args[1]) 170 } 171 commandHelp(hw, args[0], cmd, fs) 172 } else { 173 fmt.Fprintf(os.Stderr, "%s: %s\n", os.Args[0], err) 174 } 175 176 _ = clientLogout(ctx, cmd) 177 178 return rc 179} 180