func (q *QMP) ExecuteDeviceAdd(ctx context.Context, blockdevID, devID, driver, bus string) error {
	args := map[string]interface{}{
		"id":     devID,
		"driver": driver,
		"drive":  blockdevID,
	}
	if bus != "" {
		args["bus"] = bus
	}
	return q.executeCommand(ctx, "device_add", args, nil)
}
ExecuteDeviceAdd adds the guest portion of a device to a QEMU instance
using the device_add command.  blockdevID should match the blockdevID passed
to a previous call to ExecuteBlockdevAdd.  devID is the id of the device to
add.  Both strings must be valid QMP identifiers.  driver is the name of the
driver,e.g., virtio-blk-pci, and bus is the name of the bus.  bus is optional.
package qemu
import (
	"bufio"
	"container/list"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net"
	"time"
	"context"
)
type QMPLog interface {
	V(int32) bool
	Infof(string, ...interface{})
	Warningf(string, ...interface{})
	Errorf(string, ...interface{})
}
type qmpNullLogger struct{}
func (l qmpNullLogger) V(level int32) bool {
	return false
}
func (l qmpNullLogger) Infof(format string, v ...interface{}) {
}
func (l qmpNullLogger) Warningf(format string, v ...interface{}) {
}
func (l qmpNullLogger) Errorf(format string, v ...interface{}) {
}
type QMPConfig struct {
	EventCh chan<- QMPEvent
	Logger QMPLog
}
type qmpEventFilter struct {
	eventName string
	dataKey   string
	dataValue string
}
type QMPEvent struct {
	Name string
	Data map[string]interface{}
	Timestamp time.Time
}
type qmpResult struct {
	response interface{}
	err      error
}
type qmpCommand struct {
	ctx            context.Context
	res            chan qmpResult
	name           string
	args           map[string]interface{}
	filter         *qmpEventFilter
	resultReceived bool
}
type QMP struct {
	cmdCh          chan qmpCommand
	conn           io.ReadWriteCloser
	cfg            QMPConfig
	connectedCh    chan<- *QMPVersion
	disconnectedCh chan struct{}
	version        *QMPVersion
}
type QMPVersion struct {
	Major        int
	Minor        int
	Micro        int
	Capabilities []string
}
type CPUProperties struct {
	Node   int `json:"node-id"`
	Socket int `json:"socket-id"`
	Core   int `json:"core-id"`
	Thread int `json:"thread-id"`
}
type HotpluggableCPU struct {
	Type       string        `json:"type"`
	VcpusCount int           `json:"vcpus-count"`
	Properties CPUProperties `json:"props"`
	QOMPath    string        `json:"qom-path"`
}
func (q *QMP) readLoop(fromVMCh chan<- []byte) {
	scanner := bufio.NewScanner(q.conn)
	for scanner.Scan() {
		line := scanner.Bytes()
		if q.cfg.Logger.V(1) {
			q.cfg.Logger.Infof("%s", string(line))
		}
		fromVMCh <- line
	}
	close(fromVMCh)
}
func (q *QMP) processQMPEvent(cmdQueue *list.List, name interface{}, data interface{},
	timestamp interface{}) {
	strname, ok := name.(string)
	if !ok {
		return
	}
	var eventData map[string]interface{}
	if data != nil {
		eventData, _ = data.(map[string]interface{})
	}
	cmdEl := cmdQueue.Front()
	if cmdEl != nil {
		cmd := cmdEl.Value.(*qmpCommand)
		filter := cmd.filter
		if filter != nil {
			if filter.eventName == strname {
				match := filter.dataKey == ""
				if !match && eventData != nil {
					match = eventData[filter.dataKey] == filter.dataValue
				}
				if match {
					if cmd.resultReceived {
						q.finaliseCommand(cmdEl, cmdQueue, true)
					} else {
						cmd.filter = nil
					}
				}
			}
		}
	}
	if q.cfg.EventCh != nil {
		ev := QMPEvent{
			Name: strname,
			Data: eventData,
		}
		if timestamp != nil {
			timestamp, ok := timestamp.(map[string]interface{})
			if ok {
				seconds, _ := timestamp["seconds"].(float64)
				microseconds, _ := timestamp["microseconds"].(float64)
				ev.Timestamp = time.Unix(int64(seconds), int64(microseconds))
			}
		}
		q.cfg.EventCh <- ev
	}
}
func (q *QMP) finaliseCommandWithResponse(cmdEl *list.Element, cmdQueue *list.List, succeeded bool, response interface{}) {
	cmd := cmdEl.Value.(*qmpCommand)
	cmdQueue.Remove(cmdEl)
	select {
	case <-cmd.ctx.Done():
	default:
		if succeeded {
			cmd.res <- qmpResult{response: response}
		} else {
			cmd.res <- qmpResult{err: fmt.Errorf("QMP command failed")}
		}
	}
	if cmdQueue.Len() > 0 {
		q.writeNextQMPCommand(cmdQueue)
	}
}
func (q *QMP) finaliseCommand(cmdEl *list.Element, cmdQueue *list.List, succeeded bool) {
	q.finaliseCommandWithResponse(cmdEl, cmdQueue, succeeded, nil)
}
func (q *QMP) processQMPInput(line []byte, cmdQueue *list.List) {
	var vmData map[string]interface{}
	err := json.Unmarshal(line, &vmData)
	if err != nil {
		q.cfg.Logger.Warningf("Unable to decode response [%s] from VM: %v",
			string(line), err)
		return
	}
	if evname, found := vmData["event"]; found {
		q.processQMPEvent(cmdQueue, evname, vmData["data"], vmData["timestamp"])
		return
	}
	response, succeeded := vmData["return"]
	_, failed := vmData["error"]
	if !succeeded && !failed {
		return
	}
	cmdEl := cmdQueue.Front()
	if cmdEl == nil {
		q.cfg.Logger.Warningf("Unexpected command response received [%s] from VM",
			string(line))
		return
	}
	cmd := cmdEl.Value.(*qmpCommand)
	if failed || cmd.filter == nil {
		q.finaliseCommandWithResponse(cmdEl, cmdQueue, succeeded, response)
	} else {
		cmd.resultReceived = true
	}
}
func currentCommandDoneCh(cmdQueue *list.List) <-chan struct{} {
	cmdEl := cmdQueue.Front()
	if cmdEl == nil {
		return nil
	}
	cmd := cmdEl.Value.(*qmpCommand)
	return cmd.ctx.Done()
}
func (q *QMP) writeNextQMPCommand(cmdQueue *list.List) {
	cmdEl := cmdQueue.Front()
	cmd := cmdEl.Value.(*qmpCommand)
	cmdData := make(map[string]interface{})
	cmdData["execute"] = cmd.name
	if cmd.args != nil {
		cmdData["arguments"] = cmd.args
	}
	encodedCmd, err := json.Marshal(&cmdData)
	if err != nil {
		cmd.res <- qmpResult{
			err: fmt.Errorf("Unable to marhsall command %s: %v",
				cmd.name, err),
		}
		cmdQueue.Remove(cmdEl)
	}
	q.cfg.Logger.Infof("%s", string(encodedCmd))
	encodedCmd = append(encodedCmd, '\n')
	_, err = q.conn.Write(encodedCmd)
	if err != nil {
		cmd.res <- qmpResult{
			err: fmt.Errorf("Unable to write command to qmp socket %v", err),
		}
		cmdQueue.Remove(cmdEl)
	}
}
func failOutstandingCommands(cmdQueue *list.List) {
	for e := cmdQueue.Front(); e != nil; e = e.Next() {
		cmd := e.Value.(*qmpCommand)
		select {
		case cmd.res <- qmpResult{
			err: errors.New("exitting QMP loop, command cancelled"),
		}:
		case <-cmd.ctx.Done():
		}
	}
}
func (q *QMP) cancelCurrentCommand(cmdQueue *list.List) {
	cmdEl := cmdQueue.Front()
	cmd := cmdEl.Value.(*qmpCommand)
	if cmd.resultReceived {
		q.finaliseCommand(cmdEl, cmdQueue, false)
	} else {
		cmd.filter = nil
	}
}
func (q *QMP) parseVersion(version []byte) *QMPVersion {
	var qmp map[string]interface{}
	err := json.Unmarshal(version, &qmp)
	if err != nil {
		q.cfg.Logger.Errorf("Invalid QMP greeting: %s", string(version))
		return nil
	}
	versionMap := qmp
	for _, k := range []string{"QMP", "version", "qemu"} {
		versionMap, _ = versionMap[k].(map[string]interface{})
		if versionMap == nil {
			q.cfg.Logger.Errorf("Invalid QMP greeting: %s", string(version))
			return nil
		}
	}
	micro, _ := versionMap["micro"].(float64)
	minor, _ := versionMap["minor"].(float64)
	major, _ := versionMap["major"].(float64)
	capabilities, _ := qmp["QMP"].(map[string]interface{})["capabilities"].([]interface{})
	stringcaps := make([]string, 0, len(capabilities))
	for _, c := range capabilities {
		if cap, ok := c.(string); ok {
			stringcaps = append(stringcaps, cap)
		}
	}
	return &QMPVersion{Major: int(major),
		Minor:        int(minor),
		Micro:        int(micro),
		Capabilities: stringcaps,
	}
}
func (q *QMP) mainLoop() {
	cmdQueue := list.New().Init()
	fromVMCh := make(chan []byte)
	go q.readLoop(fromVMCh)
	defer func() {
		if q.cfg.EventCh != nil {
			close(q.cfg.EventCh)
		}
		_ = q.conn.Close()
		_ = <-fromVMCh
		failOutstandingCommands(cmdQueue)
		close(q.disconnectedCh)
	}()
	version := []byte{}
	var cmdDoneCh <-chan struct{}
DONE:
	for {
		var ok bool
		select {
		case cmd, ok := <-q.cmdCh:
			if !ok {
				return
			}
			_ = cmdQueue.PushBack(&cmd)
		case version, ok = <-fromVMCh:
			if !ok {
				return
			}
			if cmdQueue.Len() >= 1 {
				q.writeNextQMPCommand(cmdQueue)
				cmdDoneCh = currentCommandDoneCh(cmdQueue)
			}
			break DONE
		}
	}
	q.connectedCh <- q.parseVersion(version)
	for {
		select {
		case cmd, ok := <-q.cmdCh:
			if !ok {
				return
			}
			_ = cmdQueue.PushBack(&cmd)
			if cmdQueue.Len() == 1 {
				q.writeNextQMPCommand(cmdQueue)
				cmdDoneCh = currentCommandDoneCh(cmdQueue)
			}
		case line, ok := <-fromVMCh:
			if !ok {
				return
			}
			q.processQMPInput(line, cmdQueue)
			cmdDoneCh = currentCommandDoneCh(cmdQueue)
		case <-cmdDoneCh:
			q.cancelCurrentCommand(cmdQueue)
			cmdDoneCh = currentCommandDoneCh(cmdQueue)
		}
	}
}
func startQMPLoop(conn io.ReadWriteCloser, cfg QMPConfig,
	connectedCh chan<- *QMPVersion, disconnectedCh chan struct{}) *QMP {
	q := &QMP{
		cmdCh:          make(chan qmpCommand),
		conn:           conn,
		cfg:            cfg,
		connectedCh:    connectedCh,
		disconnectedCh: disconnectedCh,
	}
	go q.mainLoop()
	return q
}
func (q *QMP) executeCommandWithResponse(ctx context.Context, name string, args map[string]interface{},
	filter *qmpEventFilter) (interface{}, error) {
	var err error
	var response interface{}
	resCh := make(chan qmpResult)
	select {
	case <-q.disconnectedCh:
		err = errors.New("exitting QMP loop, command cancelled")
	case q.cmdCh <- qmpCommand{
		ctx:    ctx,
		res:    resCh,
		name:   name,
		args:   args,
		filter: filter,
	}:
	}
	if err != nil {
		return response, err
	}
	select {
	case res := <-resCh:
		err = res.err
		response = res.response
	case <-ctx.Done():
		err = ctx.Err()
	}
	return response, err
}
func (q *QMP) executeCommand(ctx context.Context, name string, args map[string]interface{},
	filter *qmpEventFilter) error {
	_, err := q.executeCommandWithResponse(ctx, name, args, filter)
	return err
}
func QMPStart(ctx context.Context, socket string, cfg QMPConfig, disconnectedCh chan struct{}) (*QMP, *QMPVersion, error) {
	if cfg.Logger == nil {
		cfg.Logger = qmpNullLogger{}
	}
	dialer := net.Dialer{Cancel: ctx.Done()}
	conn, err := dialer.Dial("unix", socket)
	if err != nil {
		cfg.Logger.Warningf("Unable to connect to unix socket (%s): %v", socket, err)
		close(disconnectedCh)
		return nil, nil, err
	}
	connectedCh := make(chan *QMPVersion)
	q := startQMPLoop(conn, cfg, connectedCh, disconnectedCh)
	select {
	case <-ctx.Done():
		q.Shutdown()
		<-disconnectedCh
		return nil, nil, fmt.Errorf("Canceled by caller")
	case <-disconnectedCh:
		return nil, nil, fmt.Errorf("Lost connection to VM")
	case q.version = <-connectedCh:
		if q.version == nil {
			return nil, nil, fmt.Errorf("Failed to find QMP version information")
		}
	}
	return q, q.version, nil
}
func (q *QMP) Shutdown() {
	close(q.cmdCh)
}
func (q *QMP) ExecuteQMPCapabilities(ctx context.Context) error {
	return q.executeCommand(ctx, "qmp_capabilities", nil, nil)
}
func (q *QMP) ExecuteStop(ctx context.Context) error {
	return q.executeCommand(ctx, "stop", nil, nil)
}
func (q *QMP) ExecuteCont(ctx context.Context) error {
	return q.executeCommand(ctx, "cont", nil, nil)
}
func (q *QMP) ExecuteSystemPowerdown(ctx context.Context) error {
	filter := &qmpEventFilter{
		eventName: "SHUTDOWN",
	}
	return q.executeCommand(ctx, "system_powerdown", nil, filter)
}
func (q *QMP) ExecuteQuit(ctx context.Context) error {
	return q.executeCommand(ctx, "quit", nil, nil)
}
func (q *QMP) ExecuteBlockdevAdd(ctx context.Context, device, blockdevID string) error {
	var args map[string]interface{}
	blockdevArgs := map[string]interface{}{
		"driver": "raw",
		"file": map[string]interface{}{
			"driver":   "file",
			"filename": device,
		},
	}
	if q.version.Major > 2 || (q.version.Major == 2 && q.version.Minor >= 9) {
		blockdevArgs["node-name"] = blockdevID
		args = blockdevArgs
	} else {
		blockdevArgs["id"] = blockdevID
		args = map[string]interface{}{
			"options": blockdevArgs,
		}
	}
	return q.executeCommand(ctx, "blockdev-add", args, nil)
}