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