forked from vmware/vic
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial version to support vmware-tools "lite" in pure Go. Towards: Issue vmware#742 Issue vmware#407 Issue vmware#406
- Loading branch information
Showing
11 changed files
with
1,260 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// Copyright 2016 VMware, Inc. All Rights Reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package main | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"os" | ||
|
||
"github.com/vmware/vic/pkg/vsphere/toolbox" | ||
) | ||
|
||
func main() { | ||
in := toolbox.NewBackdoorChannelIn() | ||
out := toolbox.NewBackdoorChannelOut() | ||
|
||
service := toolbox.NewService(in, out) | ||
|
||
vix := toolbox.RegisterVixRelayedCommandHandler(service) | ||
|
||
// Trigger a command start, for example: | ||
// govc guest.start -vm vm-name kill SIGHUP | ||
vix.ProcessStartCommand = func(r *toolbox.VixMsgStartProgramRequest) (int, error) { | ||
fmt.Fprintf(os.Stderr, "guest-command: %s %s\n", r.ProgramPath, r.Arguments) | ||
return -1, nil | ||
} | ||
|
||
err := service.Start() | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
defer service.Stop() | ||
|
||
service.Wait() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// Copyright 2016 VMware, Inc. All Rights Reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package toolbox | ||
|
||
import ( | ||
"errors" | ||
|
||
"github.com/vmware/vmw-guestinfo/message" | ||
"github.com/vmware/vmw-guestinfo/vmcheck" | ||
) | ||
|
||
const ( | ||
rpciProtocol uint32 = 0x49435052 | ||
tcloProtocol uint32 = 0x4f4c4354 | ||
) | ||
|
||
var ( | ||
ErrNotVirtualWorld = errors.New("not in a virtual world") | ||
) | ||
|
||
type backdoorChannel struct { | ||
protocol uint32 | ||
|
||
*message.Channel | ||
} | ||
|
||
func (b *backdoorChannel) Start() error { | ||
if !vmcheck.IsVirtualWorld() { | ||
return ErrNotVirtualWorld | ||
} | ||
|
||
channel, err := message.NewChannel(b.protocol) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
b.Channel = channel | ||
|
||
return nil | ||
} | ||
|
||
func (b *backdoorChannel) Stop() error { | ||
err := b.Channel.Close() | ||
return err | ||
} | ||
|
||
// NewBackdoorChannelOut creates a Channel for use with the RPCI protocol | ||
func NewBackdoorChannelOut() Channel { | ||
return &backdoorChannel{ | ||
protocol: rpciProtocol, | ||
} | ||
} | ||
|
||
// NewBackdoorChannelOut creates a Channel for use with the TCLO protocol | ||
func NewBackdoorChannelIn() Channel { | ||
return &backdoorChannel{ | ||
protocol: tcloProtocol, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// Copyright 2016 VMware, Inc. All Rights Reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package toolbox | ||
|
||
var _ Channel = new(backdoorChannel) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// Copyright 2016 VMware, Inc. All Rights Reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package toolbox | ||
|
||
// Channel abstracts the guest<->vmx RPC transport | ||
type Channel interface { | ||
Start() error | ||
Stop() error | ||
Send([]byte) error | ||
Receive() ([]byte, error) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
// Copyright 2016 VMware, Inc. All Rights Reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package toolbox | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"log" | ||
"net" | ||
"os" | ||
"sync" | ||
"time" | ||
) | ||
|
||
// Service receives and dispatches incoming RPC requests from the vmx | ||
type Service struct { | ||
name string | ||
in Channel | ||
out Channel | ||
handlers map[string]Handler | ||
stop chan struct{} | ||
wg *sync.WaitGroup | ||
|
||
Interval time.Duration | ||
PrimaryIP func() string | ||
} | ||
|
||
// NewService initializes a Service instance | ||
func NewService(rpcIn Channel, rpcOut Channel) *Service { | ||
s := &Service{ | ||
name: "toolbox", // Same name used by vmtoolsd | ||
in: NewTraceChannel(rpcIn), | ||
out: NewTraceChannel(rpcOut), | ||
handlers: make(map[string]Handler), | ||
wg: new(sync.WaitGroup), | ||
stop: make(chan struct{}, 1), | ||
|
||
Interval: time.Second, | ||
PrimaryIP: DefaultIP, | ||
} | ||
|
||
s.RegisterHandler("reset", s.Reset) | ||
s.RegisterHandler("ping", s.Ping) | ||
s.RegisterHandler("Set_Option", s.SetOption) | ||
|
||
return s | ||
} | ||
|
||
// Start initializes the RPC channels and starts a goroutine to listen for incoming RPC requests | ||
func (s *Service) Start() error { | ||
err := s.in.Start() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = s.out.Start() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
ticker := time.NewTicker(s.Interval) | ||
|
||
s.wg.Add(1) | ||
go func() { | ||
defer s.wg.Done() | ||
|
||
for { | ||
select { | ||
case <-ticker.C: | ||
_ = s.in.Send(nil) // POKE | ||
|
||
request, _ := s.in.Receive() | ||
|
||
if len(request) > 0 { | ||
response := s.Dispatch(request) | ||
|
||
_ = s.in.Send(response) | ||
} | ||
case <-s.stop: | ||
ticker.Stop() | ||
return | ||
} | ||
} | ||
}() | ||
|
||
return nil | ||
} | ||
|
||
// Stop cancels the RPC listener routine created via Start | ||
func (s *Service) Stop() { | ||
s.stop <- struct{}{} | ||
|
||
_ = s.in.Stop() | ||
_ = s.out.Stop() | ||
} | ||
|
||
// Wait blocks until Start returns | ||
func (s *Service) Wait() { | ||
s.wg.Wait() | ||
} | ||
|
||
// Handler is given the raw argument portion of an RPC request and returns a response | ||
type Handler func([]byte) ([]byte, error) | ||
|
||
// RegisterHandler for the given RPC name | ||
func (s *Service) RegisterHandler(name string, handler Handler) { | ||
s.handlers[name] = handler | ||
} | ||
|
||
// Dispatch an incoming RPC request to a Handler | ||
func (s *Service) Dispatch(request []byte) []byte { | ||
msg := bytes.SplitN(request, []byte{' '}, 2) | ||
name := msg[0] | ||
|
||
// Trim NULL byte terminator | ||
if len(name) > 0 && name[len(name)-1] == 0 { | ||
name = name[:len(name)-1] | ||
} | ||
|
||
handler, ok := s.handlers[string(name)] | ||
|
||
if !ok { | ||
log.Printf("unknown command: '%s'\n", name) | ||
return []byte("Unknown Command") | ||
} | ||
|
||
var args []byte | ||
if len(msg) == 2 { | ||
args = msg[1] | ||
} | ||
|
||
response, err := handler(args) | ||
if err == nil { | ||
response = append([]byte("OK "), response...) | ||
} else { | ||
log.Printf("error calling %s: %s\n", name, err) | ||
response = append([]byte("ERR "), response...) | ||
} | ||
|
||
return response | ||
} | ||
|
||
// Reset is the default Handler for reset requests | ||
func (s *Service) Reset([]byte) ([]byte, error) { | ||
return []byte("ATR " + s.name), nil | ||
} | ||
|
||
// Ping is the default Handler for ping requests | ||
func (s *Service) Ping([]byte) ([]byte, error) { | ||
return nil, nil | ||
} | ||
|
||
// SetOption is the default Handler for Set_Option requests | ||
func (s *Service) SetOption(args []byte) ([]byte, error) { | ||
opts := bytes.SplitN(args, []byte{' '}, 2) | ||
if Trace { | ||
fmt.Fprintf(os.Stderr, "set option %s=%s\n", string(opts[0]), string(opts[1])) | ||
} | ||
|
||
switch string(opts[0]) { | ||
case "broadcastIP": // TODO: const-ify | ||
if opts[1][0] == 1 { | ||
ip := s.PrimaryIP() | ||
msg := fmt.Sprintf("info-set guestinfo.ip %s", ip) | ||
return nil, s.out.Send([]byte(msg)) | ||
} | ||
default: | ||
// TODO: handle other options... | ||
} | ||
|
||
return nil, nil | ||
} | ||
|
||
// DefaultIP is used by default when responding to a Set_Option broadcastIP request | ||
// It can be overridden with the Service.PrimaryIP field | ||
func DefaultIP() string { | ||
addrs, err := net.InterfaceAddrs() | ||
if err == nil { | ||
for _, addr := range addrs { | ||
if ip, ok := addr.(*net.IPNet); ok && !ip.IP.IsLoopback() { | ||
if ip.IP.To4() != nil { | ||
return ip.IP.String() | ||
} | ||
} | ||
} | ||
} | ||
|
||
return "" | ||
} |
Oops, something went wrong.