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  }