func()

in firecracker-control/local.go [452:553]


func (s *local) newShim(ns, vmID, containerdAddress string, shimSocket *net.UnixListener, fcSocket *net.UnixListener) (*exec.Cmd, error) {
	logger := s.logger.WithField("vmID", vmID)

	args := []string{
		"-namespace", ns,
		"-address", containerdAddress,
	}

	cmd := exec.Command(internal.ShimBinaryName, args...)

	shimDir, err := vm.ShimDir(s.config.ShimBaseDir, ns, vmID)
	if err != nil {
		err = errors.Wrap(err, "failed to create shim dir")
		logger.WithError(err).Error()
		return nil, err
	}

	// note: The working dir of the shim has an effect on the length of the path
	// needed to specify various unix sockets that the shim uses to communicate
	// with the firecracker VMM and guest agent within. The length of that path
	// has a relatively low limit (usually 108 chars), so modifying the working
	// dir should be done with caution. See internal/vm/dir.go for the path
	// definitions.
	cmd.Dir = shimDir.RootPath()

	shimSocketFile, err := shimSocket.File()
	if err != nil {
		err = errors.Wrap(err, "failed to get shim socket fd")
		logger.WithError(err).Error()
		return nil, err
	}

	fcSocketFile, err := fcSocket.File()
	if err != nil {
		err = errors.Wrap(err, "failed to get shim fccontrol socket fd")
		logger.WithError(err).Error()
		return nil, err
	}

	cmd.ExtraFiles = append(cmd.ExtraFiles, shimSocketFile, fcSocketFile)
	fcSocketFDNum := 2 + len(cmd.ExtraFiles) // "2 +" because ExtraFiles come after stderr (fd #2)

	ttrpc := containerdAddress + ".ttrpc"
	cmd.Env = append(os.Environ(),
		fmt.Sprintf("%s=%s", ttrpcAddressEnv, ttrpc),
		fmt.Sprintf("%s=%s", internal.VMIDEnvVarKey, vmID),
		fmt.Sprintf("%s=%s", internal.FCSocketFDEnvKey, strconv.Itoa(fcSocketFDNum))) // TODO remove after containerd is updated to expose ttrpc server to shim

	cmd.SysProcAttr = &syscall.SysProcAttr{
		Setpgid: true,
	}

	// shim stderr is just raw text, so pass it through our logrus formatter first
	cmd.Stderr = logger.WithField("shim_stream", "stderr").WriterLevel(logrus.ErrorLevel)
	// shim stdout on the other hand is already formatted by logrus, so pass that transparently through to containerd logs
	cmd.Stdout = logger.Logger.Out

	logger.Debugf("starting %s", internal.ShimBinaryName)

	err = cmd.Start()
	if err != nil {
		err = errors.Wrap(err, "failed to start shim child process")
		logger.WithError(err).Error()
		return nil, err
	}

	// make sure to wait after start
	go func() {
		if err := cmd.Wait(); err != nil {
			if exitErr, ok := err.(*exec.ExitError); ok {
				// shim is usually terminated by cancelling the context
				logger.WithError(exitErr).Debug("shim has been terminated")
			} else {
				logger.WithError(err).Error("shim has been unexpectedly terminated")
			}
		}

		// Close all Unix sockets.
		if err := shimSocketFile.Close(); err != nil {
			logger.WithError(err).Errorf("failed to close %q", shimSocketFile.Name())
		}
		if err := fcSocketFile.Close(); err != nil {
			logger.WithError(err).Errorf("failed to close %q", fcSocketFile.Name())
		}

		if err := s.removeSockets(ns, vmID); err != nil {
			logger.WithError(err).Errorf("failed to remove sockets")
		}

		if err := os.RemoveAll(shimDir.RootPath()); err != nil {
			logger.WithError(err).Errorf("failed to remove %q", shimDir.RootPath())
		}
	}()

	err = setShimOOMScore(cmd.Process.Pid)
	if err != nil {
		logger.WithError(err).Error()
		return nil, err
	}

	return cmd, nil
}