github.com/vmware/govmomi@v0.37.2/toolbox/service.go (about) 1 /* 2 Copyright (c) 2017 VMware, Inc. All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package toolbox 18 19 import ( 20 "bytes" 21 "fmt" 22 "log" 23 "net" 24 "os" 25 "sync" 26 "time" 27 28 "github.com/vmware/govmomi/toolbox/hgfs" 29 ) 30 31 const ( 32 // TOOLS_VERSION_UNMANAGED as defined in open-vm-tools/lib/include/vm_tools_version.h 33 toolsVersionUnmanaged = 0x7fffffff 34 35 // RPCIN_MAX_DELAY as defined in rpcChannelInt.h: 36 maxDelay = 10 37 ) 38 39 var ( 40 capabilities = []string{ 41 // Without tools.set.version, the UI reports Tools are "running", but "not installed" 42 fmt.Sprintf("tools.set.version %d", toolsVersionUnmanaged), 43 44 // Required to invoke guest power operations (shutdown, reboot) 45 "tools.capability.statechange", 46 47 "tools.capability.hgfs_server toolbox 1", 48 } 49 50 netInterfaceAddrs = net.InterfaceAddrs 51 52 // If we have an RPCI send error, the channels will be reset. 53 // open-vm-tools/lib/rpcChannel/rpcChannel.c:RpcChannelCheckReset also backs off in this case 54 resetDelay = time.Duration(500) // 500 * 10ms == 5s 55 ) 56 57 // Service receives and dispatches incoming RPC requests from the vmx 58 type Service struct { 59 name string 60 in Channel 61 out *ChannelOut 62 handlers map[string]Handler 63 stop chan struct{} 64 wg *sync.WaitGroup 65 delay time.Duration 66 rpcError bool 67 68 Command *CommandServer 69 Power *PowerCommandHandler 70 71 PrimaryIP func() string 72 } 73 74 // NewService initializes a Service instance 75 func NewService(rpcIn Channel, rpcOut Channel) *Service { 76 s := &Service{ 77 name: "toolbox", // Same name used by vmtoolsd 78 in: NewTraceChannel(rpcIn), 79 out: &ChannelOut{NewTraceChannel(rpcOut)}, 80 handlers: make(map[string]Handler), 81 wg: new(sync.WaitGroup), 82 stop: make(chan struct{}), 83 84 PrimaryIP: DefaultIP, 85 } 86 87 s.RegisterHandler("reset", s.Reset) 88 s.RegisterHandler("ping", s.Ping) 89 s.RegisterHandler("Set_Option", s.SetOption) 90 s.RegisterHandler("Capabilities_Register", s.CapabilitiesRegister) 91 92 s.Command = registerCommandServer(s) 93 s.Command.FileServer = hgfs.NewServer() 94 s.Command.FileServer.RegisterFileHandler("proc", s.Command.ProcessManager) 95 s.Command.FileServer.RegisterFileHandler(hgfs.ArchiveScheme, hgfs.NewArchiveHandler()) 96 97 s.Power = registerPowerCommandHandler(s) 98 99 return s 100 } 101 102 // backoff exponentially increases the RPC poll delay up to maxDelay 103 func (s *Service) backoff() { 104 if s.delay < maxDelay { 105 if s.delay > 0 { 106 d := s.delay * 2 107 if d > s.delay && d < maxDelay { 108 s.delay = d 109 } else { 110 s.delay = maxDelay 111 } 112 } else { 113 s.delay = 1 114 } 115 } 116 } 117 118 func (s *Service) stopChannel() { 119 _ = s.in.Stop() 120 _ = s.out.Stop() 121 } 122 123 func (s *Service) startChannel() error { 124 err := s.in.Start() 125 if err != nil { 126 return err 127 } 128 129 return s.out.Start() 130 } 131 132 func (s *Service) checkReset() error { 133 if s.rpcError { 134 s.stopChannel() 135 err := s.startChannel() 136 if err != nil { 137 s.delay = resetDelay 138 return err 139 } 140 s.rpcError = false 141 } 142 143 return nil 144 } 145 146 // Start initializes the RPC channels and starts a goroutine to listen for incoming RPC requests 147 func (s *Service) Start() error { 148 err := s.startChannel() 149 if err != nil { 150 return err 151 } 152 153 s.wg.Add(1) 154 go func() { 155 defer s.wg.Done() 156 157 // Same polling interval and backoff logic as vmtoolsd. 158 // Required in our case at startup at least, otherwise it is possible 159 // we miss the 1 Capabilities_Register call for example. 160 161 // Note we Send(response) even when nil, to let the VMX know we are here 162 var response []byte 163 164 for { 165 select { 166 case <-s.stop: 167 s.stopChannel() 168 return 169 case <-time.After(time.Millisecond * 10 * s.delay): 170 if err = s.checkReset(); err != nil { 171 continue 172 } 173 174 err = s.in.Send(response) 175 response = nil 176 if err != nil { 177 s.delay = resetDelay 178 s.rpcError = true 179 continue 180 } 181 182 request, _ := s.in.Receive() 183 184 if len(request) > 0 { 185 response = s.Dispatch(request) 186 187 s.delay = 0 188 } else { 189 s.backoff() 190 } 191 } 192 } 193 }() 194 195 return nil 196 } 197 198 // Stop cancels the RPC listener routine created via Start 199 func (s *Service) Stop() { 200 close(s.stop) 201 } 202 203 // Wait blocks until Start returns, allowing any current RPC in progress to complete. 204 func (s *Service) Wait() { 205 s.wg.Wait() 206 } 207 208 // Handler is given the raw argument portion of an RPC request and returns a response 209 type Handler func([]byte) ([]byte, error) 210 211 // RegisterHandler for the given RPC name 212 func (s *Service) RegisterHandler(name string, handler Handler) { 213 s.handlers[name] = handler 214 } 215 216 // Dispatch an incoming RPC request to a Handler 217 func (s *Service) Dispatch(request []byte) []byte { 218 msg := bytes.SplitN(request, []byte{' '}, 2) 219 name := msg[0] 220 221 // Trim NULL byte terminator 222 name = bytes.TrimRight(name, "\x00") 223 224 handler, ok := s.handlers[string(name)] 225 226 if !ok { 227 log.Printf("unknown command: %q\n", name) 228 return []byte("Unknown Command") 229 } 230 231 var args []byte 232 if len(msg) == 2 { 233 args = msg[1] 234 } 235 236 response, err := handler(args) 237 if err == nil { 238 response = append([]byte("OK "), response...) 239 } else { 240 log.Printf("error calling %s: %s\n", name, err) 241 response = append([]byte("ERR "), response...) 242 } 243 244 return response 245 } 246 247 // Reset is the default Handler for reset requests 248 func (s *Service) Reset([]byte) ([]byte, error) { 249 s.SendGuestInfo() // Send the IP info ASAP 250 251 return []byte("ATR " + s.name), nil 252 } 253 254 // Ping is the default Handler for ping requests 255 func (s *Service) Ping([]byte) ([]byte, error) { 256 return nil, nil 257 } 258 259 // SetOption is the default Handler for Set_Option requests 260 func (s *Service) SetOption(args []byte) ([]byte, error) { 261 opts := bytes.SplitN(args, []byte{' '}, 2) 262 key := string(opts[0]) 263 val := string(opts[1]) 264 265 if Trace { 266 fmt.Fprintf(os.Stderr, "set option %q=%q\n", key, val) 267 } 268 269 switch key { 270 case "broadcastIP": // TODO: const-ify 271 if val == "1" { 272 ip := s.PrimaryIP() 273 if ip == "" { 274 log.Printf("failed to find primary IP") 275 return nil, nil 276 } 277 msg := fmt.Sprintf("info-set guestinfo.ip %s", ip) 278 _, err := s.out.Request([]byte(msg)) 279 if err != nil { 280 return nil, err 281 } 282 283 s.SendGuestInfo() 284 } 285 default: 286 // TODO: handle other options... 287 } 288 289 return nil, nil 290 } 291 292 // DefaultIP is used by default when responding to a Set_Option broadcastIP request 293 // It can be overridden with the Service.PrimaryIP field 294 func DefaultIP() string { 295 addrs, err := netInterfaceAddrs() 296 if err == nil { 297 for _, addr := range addrs { 298 if ip, ok := addr.(*net.IPNet); ok && !ip.IP.IsLoopback() { 299 if ip.IP.To4() != nil { 300 return ip.IP.String() 301 } 302 } 303 } 304 } 305 306 return "" 307 } 308 309 func (s *Service) CapabilitiesRegister([]byte) ([]byte, error) { 310 for _, cap := range capabilities { 311 _, err := s.out.Request([]byte(cap)) 312 if err != nil { 313 log.Printf("send %q: %s", cap, err) 314 } 315 } 316 317 return nil, nil 318 } 319 320 func (s *Service) SendGuestInfo() { 321 info := []func() ([]byte, error){ 322 GuestInfoNicInfoRequest, 323 } 324 325 for i, r := range info { 326 b, err := r() 327 328 if err == nil { 329 _, err = s.out.Request(b) 330 } 331 332 if err != nil { 333 log.Printf("SendGuestInfo %d: %s", i, err) 334 } 335 } 336 }