github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/shim.go (about)

     1  // Copyright (c) 2017 Intel Corporation
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  //
     5  
     6  package virtcontainers
     7  
     8  import (
     9  	"fmt"
    10  	"os"
    11  	"os/exec"
    12  	"syscall"
    13  	"time"
    14  
    15  	ns "github.com/kata-containers/runtime/virtcontainers/pkg/nsenter"
    16  	"github.com/kata-containers/runtime/virtcontainers/types"
    17  	"github.com/mitchellh/mapstructure"
    18  	"github.com/sirupsen/logrus"
    19  )
    20  
    21  // ShimType describes a shim type.
    22  type ShimType string
    23  
    24  const (
    25  	// NoopShimType is the noopShim.
    26  	NoopShimType ShimType = "noopShim"
    27  
    28  	// KataShimType is the Kata Containers shim type.
    29  	KataShimType ShimType = "kataShim"
    30  
    31  	// KataBuiltInShimType is the Kata Containers builtin shim type.
    32  	KataBuiltInShimType ShimType = "kataBuiltInShim"
    33  )
    34  
    35  var waitForShimTimeout = 10.0
    36  var consoleFileMode = os.FileMode(0660)
    37  
    38  // ShimParams is the structure providing specific parameters needed
    39  // for the execution of the shim binary.
    40  type ShimParams struct {
    41  	Container  string
    42  	Token      string
    43  	URL        string
    44  	Console    string
    45  	ConsoleURL string
    46  	Terminal   bool
    47  	Detach     bool
    48  	PID        int
    49  	CreateNS   []ns.NSType
    50  	EnterNS    []ns.Namespace
    51  }
    52  
    53  // ShimConfig is the structure providing specific configuration
    54  // for shim implementations.
    55  type ShimConfig struct {
    56  	Path  string
    57  	Debug bool
    58  	Trace bool
    59  }
    60  
    61  // Set sets a shim type based on the input string.
    62  func (pType *ShimType) Set(value string) error {
    63  	switch value {
    64  	case "noopShim":
    65  		*pType = NoopShimType
    66  	case "kataShim":
    67  		*pType = KataShimType
    68  	case "kataBuiltInShim":
    69  		*pType = KataBuiltInShimType
    70  	default:
    71  		return fmt.Errorf("Unknown shim type %s", value)
    72  	}
    73  	return nil
    74  }
    75  
    76  // String converts a shim type to a string.
    77  func (pType *ShimType) String() string {
    78  	switch *pType {
    79  	case NoopShimType:
    80  		return string(NoopShimType)
    81  	case KataShimType:
    82  		return string(KataShimType)
    83  	case KataBuiltInShimType:
    84  		return string(KataBuiltInShimType)
    85  	default:
    86  		return ""
    87  	}
    88  }
    89  
    90  // newShim returns a shim from a shim type.
    91  func newShim(pType ShimType) (shim, error) {
    92  	switch pType {
    93  	case NoopShimType:
    94  		return &noopShim{}, nil
    95  	case KataShimType:
    96  		return &kataShim{}, nil
    97  	case KataBuiltInShimType:
    98  		return &kataBuiltInShim{}, nil
    99  	default:
   100  		return &noopShim{}, nil
   101  	}
   102  }
   103  
   104  // newShimConfig returns a shim config from a generic SandboxConfig interface.
   105  func newShimConfig(config SandboxConfig) interface{} {
   106  	switch config.ShimType {
   107  	case NoopShimType, KataBuiltInShimType:
   108  		return nil
   109  	case KataShimType:
   110  		var shimConfig ShimConfig
   111  		err := mapstructure.Decode(config.ShimConfig, &shimConfig)
   112  		if err != nil {
   113  			return err
   114  		}
   115  		return shimConfig
   116  	default:
   117  		return nil
   118  	}
   119  }
   120  
   121  func shimLogger() *logrus.Entry {
   122  	return virtLog.WithField("subsystem", "shim")
   123  }
   124  
   125  func signalShim(pid int, sig syscall.Signal) error {
   126  	if pid <= 0 {
   127  		return nil
   128  	}
   129  
   130  	shimLogger().WithFields(
   131  		logrus.Fields{
   132  			"shim-pid":    pid,
   133  			"shim-signal": sig,
   134  		}).Info("Signalling shim")
   135  
   136  	return syscall.Kill(pid, sig)
   137  }
   138  
   139  func stopShim(pid int) error {
   140  	if pid <= 0 {
   141  		return nil
   142  	}
   143  
   144  	if err := signalShim(pid, syscall.SIGKILL); err != nil && err != syscall.ESRCH {
   145  		return err
   146  	}
   147  
   148  	return nil
   149  }
   150  
   151  func prepareAndStartShim(sandbox *Sandbox, shim shim, cid, token, url, consoleURL string, cmd types.Cmd,
   152  	createNSList []ns.NSType, enterNSList []ns.Namespace) (*Process, error) {
   153  	process := &Process{
   154  		Token:     token,
   155  		StartTime: time.Now().UTC(),
   156  	}
   157  
   158  	shimParams := ShimParams{
   159  		Container:  cid,
   160  		Token:      token,
   161  		URL:        url,
   162  		Console:    cmd.Console,
   163  		Terminal:   cmd.Interactive,
   164  		Detach:     cmd.Detach,
   165  		CreateNS:   createNSList,
   166  		EnterNS:    enterNSList,
   167  		ConsoleURL: consoleURL,
   168  	}
   169  
   170  	pid, err := shim.start(sandbox, shimParams)
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  
   175  	process.Pid = pid
   176  
   177  	return process, nil
   178  }
   179  
   180  func startShim(args []string, params ShimParams) (int, error) {
   181  	cmd := exec.Command(args[0], args[1:]...)
   182  
   183  	if !params.Detach {
   184  		cmd.Stdin = os.Stdin
   185  		cmd.Stdout = os.Stdout
   186  		cmd.Stderr = os.Stderr
   187  	}
   188  
   189  	cloneFlags := 0
   190  	for _, nsType := range params.CreateNS {
   191  		cloneFlags |= ns.CloneFlagsTable[nsType]
   192  	}
   193  
   194  	cmd.SysProcAttr = &syscall.SysProcAttr{
   195  		Cloneflags: uintptr(cloneFlags),
   196  	}
   197  
   198  	var f *os.File
   199  	var err error
   200  	if params.Console != "" {
   201  		f, err = os.OpenFile(params.Console, os.O_RDWR, consoleFileMode)
   202  		if err != nil {
   203  			return -1, err
   204  		}
   205  
   206  		cmd.Stdin = f
   207  		cmd.Stdout = f
   208  		cmd.Stderr = f
   209  		// Create Session
   210  		cmd.SysProcAttr.Setsid = true
   211  	}
   212  	defer func() {
   213  		if f != nil {
   214  			f.Close()
   215  		}
   216  	}()
   217  
   218  	if err := ns.NsEnter(params.EnterNS, func() error {
   219  		return cmd.Start()
   220  	}); err != nil {
   221  		return -1, err
   222  	}
   223  
   224  	return cmd.Process.Pid, nil
   225  }
   226  
   227  func isShimRunning(pid int) (bool, error) {
   228  	if pid <= 0 {
   229  		return false, nil
   230  	}
   231  
   232  	process, err := os.FindProcess(pid)
   233  	if err != nil {
   234  		return false, err
   235  	}
   236  
   237  	if err := process.Signal(syscall.Signal(0)); err != nil {
   238  		return false, nil
   239  	}
   240  
   241  	return true, nil
   242  }
   243  
   244  // waitForShim waits for the end of the shim unless it reaches the timeout
   245  // first, returning an error in that case.
   246  func waitForShim(pid int) error {
   247  	if pid <= 0 {
   248  		return nil
   249  	}
   250  
   251  	tInit := time.Now()
   252  	for {
   253  		running, err := isShimRunning(pid)
   254  		if err != nil {
   255  			return err
   256  		}
   257  
   258  		if !running {
   259  			break
   260  		}
   261  
   262  		if time.Since(tInit).Seconds() >= waitForShimTimeout {
   263  			return fmt.Errorf("Shim still running, timeout %f s has been reached", waitForShimTimeout)
   264  		}
   265  
   266  		// Let's avoid to run a too busy loop
   267  		time.Sleep(time.Duration(100) * time.Millisecond)
   268  	}
   269  
   270  	return nil
   271  }
   272  
   273  // shim is the virtcontainers shim interface.
   274  type shim interface {
   275  	// start starts the shim relying on its configuration and on
   276  	// parameters provided.
   277  	start(sandbox *Sandbox, params ShimParams) (int, error)
   278  }