Skip to content
This repository has been archived by the owner on May 12, 2021. It is now read-only.

Commit

Permalink
Merge pull request #510 from amshinde/my-1.5-backport
Browse files Browse the repository at this point in the history
channel: Check for channel type in kernel cmdline options
  • Loading branch information
Eric Ernst authored Apr 3, 2019
2 parents 6692613 + 6214269 commit 5f3fa74
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 20 deletions.
16 changes: 16 additions & 0 deletions agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,22 @@ var debug = false
// if true, coredump when an internal error occurs or a fatal signal is received
var crashOnError = false

// commType is used to denote the communication channel type used.
type commType int

const (
// virtio-serial channel
serialCh commType = iota

// vsock channel
vsockCh

// channel type not passed explicitly
unknownCh
)

var commCh = unknownCh

// This is the list of file descriptors we can properly close after the process
// has been started. When the new process is exec(), those file descriptors are
// duplicated and it is our responsibility to close them since we have opened
Expand Down
102 changes: 82 additions & 20 deletions channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,29 @@ type channel interface {
// If there are neither vsocks nor serial ports, an error is returned.
func newChannel() (channel, error) {
var serialErr error
var serialPath string
var vsockErr error
var vSockSupported bool
var ch channel

for i := 0; i < channelExistMaxTries; i++ {
// check vsock path
if _, err := os.Stat(vSockDevPath); err == nil {
if vSockSupported, vsockErr = isAFVSockSupportedFunc(); vSockSupported && vsockErr == nil {
return &vSockChannel{}, nil
switch commCh {
case serialCh:
if ch, serialErr = checkForSerialChannel(); serialErr == nil && ch.(*serialChannel) != nil {
return ch, nil
}
case vsockCh:
if ch, vsockErr = checkForVsockChannel(); vsockErr == nil && ch.(*vSockChannel) != nil {
return ch, nil
}
}

// Check serial port path
if serialPath, serialErr = findVirtualSerialPath(serialChannelName); serialErr == nil {
return &serialChannel{serialPath: serialPath}, nil
case unknownCh:
// If we have not been explicitly passed if vsock is used or not, maybe due to
// an older runtime, try to check for vsock support.
if ch, vsockErr = checkForVsockChannel(); vsockErr == nil && ch.(*vSockChannel) != nil {
return ch, nil
}
if ch, serialErr = checkForSerialChannel(); serialErr == nil && ch.(*serialChannel) != nil {
return ch, nil
}
}

time.Sleep(channelExistWaitTime)
Expand All @@ -77,6 +85,32 @@ func newChannel() (channel, error) {
return nil, fmt.Errorf("Neither vsocks nor serial ports were found")
}

func checkForSerialChannel() (*serialChannel, error) {
// Check serial port path
serialPath, serialErr := findVirtualSerialPath(serialChannelName)
if serialErr == nil {
agentLog.Debug("Serial channel type detected")
return &serialChannel{serialPath: serialPath}, nil
}

return nil, serialErr
}

func checkForVsockChannel() (*vSockChannel, error) {
// check vsock path
if _, err := os.Stat(vSockDevPath); err != nil {
return nil, err
}

vSockSupported, vsockErr := isAFVSockSupportedFunc()
if vSockSupported && vsockErr == nil {
agentLog.Debug("Vsock channel type detected")
return &vSockChannel{}, nil
}

return nil, fmt.Errorf("Vsock not found : %s", vsockErr)
}

type vSockChannel struct {
}

Expand Down Expand Up @@ -221,23 +255,51 @@ func (c *serialChannel) teardown() error {
return c.serialConn.Close()
}

// isAFVSockSupported checks if vsock channel is used by the runtime
// by checking for devices under the vhost-vsock driver path.
// It returns true if a device is found for the vhost-vsock driver.
func isAFVSockSupported() (bool, error) {
fd, err := unix.Socket(unix.AF_VSOCK, unix.SOCK_STREAM, 0)
if err != nil {
// This case is valid. It means AF_VSOCK is not a supported
// domain on this system.
if err == unix.EAFNOSUPPORT {
return false, nil
}
// Driver path for virtio-vsock
sysVsockPath := "/sys/bus/virtio/drivers/vmw_vsock_virtio_transport/"

files, err := ioutil.ReadDir(sysVsockPath)

// This should not happen for a hypervisor with vsock driver
if err != nil {
return false, err
}

if err := unix.Close(fd); err != nil {
return true, err
// standard driver files that should be ignored
driverFiles := []string{"bind", "uevent", "unbind"}

for _, file := range files {
for _, f := range driverFiles {
if file.Name() == f {
continue
}
}

fPath := filepath.Join(sysVsockPath, file.Name())
fInfo, err := os.Lstat(fPath)
if err != nil {
return false, err
}

if fInfo.Mode()&os.ModeSymlink == 0 {
continue
}

link, err := os.Readlink(fPath)
if err != nil {
return false, err
}

if strings.Contains(link, "devices") {
return true, nil
}
}

return true, nil
return false, nil
}

func findVirtualSerialPath(serialName string) (string, error) {
Expand Down
14 changes: 14 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package main

import (
"io/ioutil"
"strconv"
"strings"

"github.com/sirupsen/logrus"
Expand All @@ -20,6 +21,7 @@ const (
logLevelFlag = optionPrefix + "log"
devModeFlag = optionPrefix + "devmode"
kernelCmdlineFile = "/proc/cmdline"
useVsockFlag = optionPrefix + "use_vsock"
)

type agentConfig struct {
Expand Down Expand Up @@ -94,6 +96,18 @@ func (c *agentConfig) parseCmdlineOption(option string) error {
if level == logrus.DebugLevel {
debug = true
}
case useVsockFlag:
flag, err := strconv.ParseBool(split[valuePosition])
if err != nil {
return err
}
if flag {
agentLog.Debug("Param passed to use vsock channel")
commCh = vsockCh
} else {
agentLog.Debug("Param passed to NOT use vsock channel")
commCh = serialCh
}
default:
if strings.HasPrefix(split[optionPosition], optionPrefix) {
return grpcStatus.Errorf(codes.NotFound, "Unknown option %s", split[optionPosition])
Expand Down
43 changes: 43 additions & 0 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,46 @@ func TestSetGrpcTrace(t *testing.T) {

assert.True(s.enableGrpcTrace, "grpc trace should be enabled")
}

func TestParseCmdlineOptionWrongOptionVsock(t *testing.T) {
t.Skip()
assert := assert.New(t)

a := &agentConfig{}

wrongOption := "use_vsockkk=true"

err := a.parseCmdlineOption(wrongOption)
assert.Errorf(err, "Parsing should fail because wrong option %q", wrongOption)
}

func TestParseCmdlineOptionsVsock(t *testing.T) {
assert := assert.New(t)

a := &agentConfig{}

type testData struct {
val string
shouldErr bool
expectedCommCh commType
}

data := []testData{
{"true", false, vsockCh},
{"false", false, serialCh},
{"blah", true, unknownCh},
}

for _, d := range data {
commCh = unknownCh
option := useVsockFlag + "=" + d.val

err := a.parseCmdlineOption(option)
if d.shouldErr {
assert.Error(err)
} else {
assert.NoError(err)
}
assert.Equal(commCh, d.expectedCommCh)
}
}

0 comments on commit 5f3fa74

Please sign in to comment.