func (h *Hyperstart) WaitForPAE(containerID, processID string) (*PAECommand, error) {
	if h.ctlMulticast == nil {
		return nil, fmt.Errorf("No multicast available for CTL channel")
	}
	channel, err := h.ctlMulticast.listen(containerID, processID, eventType)
	if err != nil {
		return nil, err
	}
	msg := <-channel
	var paeData PAECommand
	err = json.Unmarshal(msg.Message, paeData)
	if err != nil {
		return nil, err
	}
	return &paeData, nil
}
WaitForPAE waits for a PROCESSASYNCEVENT message on CTL channel.
package hyperstart
import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"math"
	"net"
	"sync"
	"time"
	"github.com/sirupsen/logrus"
)
const (
	Version         = "version"
	StartPod        = "startpod"
	DestroyPod      = "destroypod"
	ExecCmd         = "execcmd"
	Ready           = "ready"
	Ack             = "ack"
	Error           = "error"
	WinSize         = "winsize"
	Ping            = "ping"
	FinishPod       = "finishpod"
	Next            = "next"
	WriteFile       = "writefile"
	ReadFile        = "readfile"
	NewContainer    = "newcontainer"
	KillContainer   = "killcontainer"
	OnlineCPUMem    = "onlinecpumem"
	SetupInterface  = "setupinterface"
	SetupRoute      = "setuproute"
	RemoveContainer = "removecontainer"
	PsContainer     = "pscontainer"
)
var CodeList = map[string]uint32{
	Version:         VersionCode,
	StartPod:        StartPodCode,
	DestroyPod:      DestroyPodCode,
	ExecCmd:         ExecCmdCode,
	Ready:           ReadyCode,
	Ack:             AckCode,
	Error:           ErrorCode,
	WinSize:         WinsizeCode,
	Ping:            PingCode,
	Next:            NextCode,
	WriteFile:       WriteFileCode,
	ReadFile:        ReadFileCode,
	NewContainer:    NewContainerCode,
	KillContainer:   KillContainerCode,
	OnlineCPUMem:    OnlineCPUMemCode,
	SetupInterface:  SetupInterfaceCode,
	SetupRoute:      SetupRouteCode,
	RemoveContainer: RemoveContainerCode,
	PsContainer:     PsContainerCode,
}
const (
	CtlHdrSize      = 8
	CtlHdrLenOffset = 4
)
const (
	TtyHdrSize      = 12
	TtyHdrLenOffset = 8
)
type connState struct {
	sync.Mutex
	opened bool
}
func (c *connState) close() {
	c.Lock()
	defer c.Unlock()
	c.opened = false
}
func (c *connState) open() {
	c.Lock()
	defer c.Unlock()
	c.opened = true
}
func (c *connState) closed() bool {
	c.Lock()
	defer c.Unlock()
	return !c.opened
}
type Hyperstart struct {
	ctlSerial, ioSerial string
	sockType            string
	ctl, io             net.Conn
	ctlState, ioState   connState
	ctlMutex sync.Mutex
	ctlMulticast *multicast
	ctlChDone chan interface{}
}
var hyperLog = logrus.FieldLogger(logrus.New())
func SetLogger(logger logrus.FieldLogger) {
	hyperLog = logger.WithField("source", "virtcontainers/hyperstart")
}
func NewHyperstart(ctlSerial, ioSerial, sockType string) *Hyperstart {
	return &Hyperstart{
		ctlSerial: ctlSerial,
		ioSerial:  ioSerial,
		sockType:  sockType,
	}
}
func (h *Hyperstart) GetCtlSock() net.Conn {
	return h.ctl
}
func (h *Hyperstart) GetIoSock() net.Conn {
	return h.io
}
func (h *Hyperstart) GetCtlSockPath() string {
	return h.ctlSerial
}
func (h *Hyperstart) GetIoSockPath() string {
	return h.ioSerial
}
func (h *Hyperstart) GetSockType() string {
	return h.sockType
}
func (h *Hyperstart) OpenSocketsNoMulticast() error {
	var err error
	h.ctl, err = net.Dial(h.sockType, h.ctlSerial)
	if err != nil {
		return err
	}
	h.ctlState.open()
	h.io, err = net.Dial(h.sockType, h.ioSerial)
	if err != nil {
		h.ctl.Close()
		return err
	}
	h.ioState.open()
	return nil
}
func (h *Hyperstart) OpenSockets() error {
	if err := h.OpenSocketsNoMulticast(); err != nil {
		return err
	}
	h.ctlChDone = make(chan interface{})
	h.ctlMulticast = startCtlMonitor(h.ctl, h.ctlChDone)
	return nil
}
func (h *Hyperstart) CloseSockets() error {
	if !h.ctlState.closed() {
		if h.ctlChDone != nil {
			select {
			case <-h.ctlChDone:
				break
			case <-time.After(time.Duration(3) * time.Second):
				return fmt.Errorf("CTL channel did not end as expected")
			}
		}
		err := h.ctl.Close()
		if err != nil {
			return err
		}
		h.ctlState.close()
	}
	if !h.ioState.closed() {
		err := h.io.Close()
		if err != nil {
			return err
		}
		h.ioState.close()
	}
	h.ctlMulticast = nil
	return nil
}
func (h *Hyperstart) SetDeadline(t time.Time) error {
	err := h.ctl.SetDeadline(t)
	if err != nil {
		return err
	}
	return nil
}
func (h *Hyperstart) IsStarted() bool {
	ret := false
	timeoutDuration := 1 * time.Second
	if h.ctlState.closed() {
		return ret
	}
	h.SetDeadline(time.Now().Add(timeoutDuration))
	_, err := h.SendCtlMessage(Ping, nil)
	if err == nil {
		ret = true
	}
	h.SetDeadline(time.Time{})
	if ret == false {
		h.CloseSockets()
	}
	return ret
}
func FormatMessage(payload interface{}) ([]byte, error) {
	var payloadSlice []byte
	var err error
	if payload != nil {
		switch p := payload.(type) {
		case string:
			payloadSlice = []byte(p)
		default:
			payloadSlice, err = json.Marshal(p)
			if err != nil {
				return nil, err
			}
		}
	}
	return payloadSlice, nil
}
func ReadCtlMessage(conn net.Conn) (*DecodedMessage, error) {
	needRead := CtlHdrSize
	length := 0
	read := 0
	buf := make([]byte, 512)
	res := []byte{}
	for read < needRead {
		want := needRead - read
		if want > 512 {
			want = 512
		}
		nr, err := conn.Read(buf[:want])
		if err != nil {
			return nil, err
		}
		res = append(res, buf[:nr]...)
		read = read + nr
		if length == 0 && read >= CtlHdrSize {
			length = int(binary.BigEndian.Uint32(res[CtlHdrLenOffset:CtlHdrSize]))
			if length > CtlHdrSize {
				needRead = length
			}
		}
	}
	return &DecodedMessage{
		Code:    binary.BigEndian.Uint32(res[:CtlHdrLenOffset]),
		Message: res[CtlHdrSize:],
	}, nil
}
func (h *Hyperstart) WriteCtlMessage(conn net.Conn, m *DecodedMessage) error {
	length := len(m.Message) + CtlHdrSize
	if length > 10240 {
		return fmt.Errorf("message too long %d", length)
	}
	msg := make([]byte, length)
	binary.BigEndian.PutUint32(msg[:], uint32(m.Code))
	binary.BigEndian.PutUint32(msg[CtlHdrLenOffset:], uint32(length))
	copy(msg[CtlHdrSize:], m.Message)
	_, err := conn.Write(msg)
	if err != nil {
		return err
	}
	return nil
}
func ReadIoMessageWithConn(conn net.Conn) (*TtyMessage, error) {
	needRead := TtyHdrSize
	length := 0
	read := 0
	buf := make([]byte, 512)
	res := []byte{}
	for read < needRead {
		want := needRead - read
		if want > 512 {
			want = 512
		}
		nr, err := conn.Read(buf[:want])
		if err != nil {
			return nil, err
		}
		res = append(res, buf[:nr]...)
		read = read + nr
		if length == 0 && read >= TtyHdrSize {
			length = int(binary.BigEndian.Uint32(res[TtyHdrLenOffset:TtyHdrSize]))
			if length > TtyHdrSize {
				needRead = length
			}
		}
	}
	return &TtyMessage{
		Session: binary.BigEndian.Uint64(res[:TtyHdrLenOffset]),
		Message: res[TtyHdrSize:],
	}, nil
}
func (h *Hyperstart) ReadIoMessage() (*TtyMessage, error) {
	return ReadIoMessageWithConn(h.io)
}
func SendIoMessageWithConn(conn net.Conn, ttyMsg *TtyMessage) error {
	length := len(ttyMsg.Message) + TtyHdrSize
	if length > 10240 {
		return fmt.Errorf("message too long %d", length)
	}
	msg := make([]byte, length)
	binary.BigEndian.PutUint64(msg[:], ttyMsg.Session)
	binary.BigEndian.PutUint32(msg[TtyHdrLenOffset:], uint32(length))
	copy(msg[TtyHdrSize:], ttyMsg.Message)
	n, err := conn.Write(msg)
	if err != nil {
		return err
	}
	if n != length {
		return fmt.Errorf("%d bytes written out of %d expected", n, length)
	}
	return nil
}
func (h *Hyperstart) SendIoMessage(ttyMsg *TtyMessage) error {
	return SendIoMessageWithConn(h.io, ttyMsg)
}
func (h *Hyperstart) CodeFromCmd(cmd string) (uint32, error) {
	_, ok := CodeList[cmd]
	if ok == false {
		return math.MaxUint32, fmt.Errorf("unknown command '%s'", cmd)
	}
	return CodeList[cmd], nil
}
func (h *Hyperstart) CheckReturnedCode(recvMsg *DecodedMessage, expectedCode uint32) error {
	if recvMsg.Code != expectedCode {
		if recvMsg.Code == ErrorCode {
			return fmt.Errorf("ERROR received from VM agent, control msg received : %s", recvMsg.Message)
		}
		return fmt.Errorf("CMD ID received %d not matching expected %d, control msg received : %s", recvMsg.Code, expectedCode, recvMsg.Message)
	}
	return nil
}
func (h *Hyperstart) WaitForReady() error {
	if h.ctlMulticast == nil {
		return fmt.Errorf("No multicast available for CTL channel")
	}
	channel, err := h.ctlMulticast.listen("", "", replyType)
	if err != nil {
		return err
	}
	msg := <-channel
	err = h.CheckReturnedCode(msg, ReadyCode)
	if err != nil {
		return err
	}
	return nil
}