in machine.go [492:584]
func (m *Machine) startVMM(ctx context.Context) error {
m.logger.Printf("Called startVMM(), setting up a VMM on %s", m.Cfg.SocketPath)
startCmd := m.cmd.Start
m.logger.Debugf("Starting %v", m.cmd.Args)
var err error
if m.Cfg.NetNS != "" && m.Cfg.JailerCfg == nil {
// If the VM needs to be started in a netns but no jailer netns was configured,
// start the vmm child process in the netns directly here.
err = ns.WithNetNSPath(m.Cfg.NetNS, func(_ ns.NetNS) error {
return startCmd()
})
} else {
// Else, just start the process normally as it's either not in a netns or will
// be placed in one by the jailer process instead.
err = startCmd()
}
if err != nil {
m.logger.Errorf("Failed to start VMM: %s", err)
m.fatalErr = err
close(m.exitCh)
return err
}
m.logger.Debugf("VMM started socket path is %s", m.Cfg.SocketPath)
m.cleanupFuncs = append(m.cleanupFuncs,
func() error {
if err := os.Remove(m.Cfg.SocketPath); !os.IsNotExist(err) {
return err
}
return nil
},
)
errCh := make(chan error)
go func() {
waitErr := m.cmd.Wait()
if waitErr != nil {
m.logger.Warnf("firecracker exited: %s", waitErr.Error())
} else {
m.logger.Printf("firecracker exited: status=0")
}
cleanupErr := m.doCleanup()
if cleanupErr != nil {
m.logger.Errorf("failed to cleanup after VM exit: %v", cleanupErr)
}
errCh <- multierror.Append(waitErr, cleanupErr).ErrorOrNil()
// Notify subscribers that there will be no more values.
// When err is nil, two reads are performed (waitForSocket and close exitCh goroutine),
// second one never ends as it tries to read from empty channel.
close(errCh)
}()
m.setupSignals()
// Wait for firecracker to initialize:
err = m.waitForSocket(time.Duration(m.client.firecrackerInitTimeout)*time.Second, errCh)
if err != nil {
err = errors.Wrapf(err, "Firecracker did not create API socket %s", m.Cfg.SocketPath)
m.fatalErr = err
close(m.exitCh)
return err
}
// This goroutine is used to kill the process by context cancelletion,
// but doesn't tell anyone about that.
go func() {
<-ctx.Done()
err := m.stopVMM()
if err != nil {
m.logger.WithError(err).Errorf("failed to stop vm %q", m.Cfg.VMID)
}
}()
// This goroutine is used to tell clients that the process is stopped
// (gracefully or not).
go func() {
m.fatalErr = <-errCh
m.logger.Debugf("closing the exitCh %v", m.fatalErr)
close(m.exitCh)
}()
m.logger.Debugf("returning from startVMM()")
return nil
}