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  }