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
}