diff --git a/qemu/qmp.go b/qemu/qmp.go index 7af46f0b9..1e57aaa9e 100644 --- a/qemu/qmp.go +++ b/qemu/qmp.go @@ -24,6 +24,8 @@ import ( "fmt" "io" "net" + "os" + "syscall" "time" "context" @@ -118,6 +120,7 @@ type qmpCommand struct { args map[string]interface{} filter *qmpEventFilter resultReceived bool + oob []byte } // QMP is a structure that contains the internal state used by startQMPLoop and @@ -302,7 +305,12 @@ func (q *QMP) writeNextQMPCommand(cmdQueue *list.List) { } q.cfg.Logger.Infof("%s", string(encodedCmd)) encodedCmd = append(encodedCmd, '\n') - _, err = q.conn.Write(encodedCmd) + if unixConn, ok := q.conn.(*net.UnixConn); ok && len(cmd.oob) > 0 { + _, _, err = unixConn.WriteMsgUnix(encodedCmd, cmd.oob, nil) + } else { + _, err = q.conn.Write(encodedCmd) + } + if err != nil { cmd.res <- qmpResult{ err: fmt.Errorf("Unable to write command to qmp socket %v", err), @@ -486,7 +494,7 @@ func startQMPLoop(conn io.ReadWriteCloser, cfg QMPConfig, } func (q *QMP) executeCommandWithResponse(ctx context.Context, name string, args map[string]interface{}, - filter *qmpEventFilter) (interface{}, error) { + oob []byte, filter *qmpEventFilter) (interface{}, error) { var err error var response interface{} resCh := make(chan qmpResult) @@ -499,6 +507,7 @@ func (q *QMP) executeCommandWithResponse(ctx context.Context, name string, args name: name, args: args, filter: filter, + oob: oob, }: } @@ -520,7 +529,7 @@ func (q *QMP) executeCommandWithResponse(ctx context.Context, name string, args func (q *QMP) executeCommand(ctx context.Context, name string, args map[string]interface{}, filter *qmpEventFilter) error { - _, err := q.executeCommandWithResponse(ctx, name, args, filter) + _, err := q.executeCommandWithResponse(ctx, name, args, nil, filter) return err } @@ -855,7 +864,7 @@ func (q *QMP) ExecuteCPUDeviceAdd(ctx context.Context, driver, cpuID, socketID, // ExecuteQueryHotpluggableCPUs returns a slice with the list of hotpluggable CPUs func (q *QMP) ExecuteQueryHotpluggableCPUs(ctx context.Context) ([]HotpluggableCPU, error) { - response, err := q.executeCommandWithResponse(ctx, "query-hotpluggable-cpus", nil, nil) + response, err := q.executeCommandWithResponse(ctx, "query-hotpluggable-cpus", nil, nil, nil) if err != nil { return nil, err } @@ -929,11 +938,28 @@ func (q *QMP) ExecHotplugMemory(ctx context.Context, qomtype, id, mempath string } // ExecutePCIVSockAdd adds a vhost-vsock-pci bus -func (q *QMP) ExecutePCIVSockAdd(ctx context.Context, id, guestCID string) error { +func (q *QMP) ExecutePCIVSockAdd(ctx context.Context, id, guestCID, vhostfd string, disableModern bool) error { args := map[string]interface{}{ "driver": VHostVSockPCI, "id": id, "guest-cid": guestCID, + "vhostfd": vhostfd, } + + if disableModern { + args["disable-modern"] = disableModern + } + return q.executeCommand(ctx, "device_add", args, nil) } + +// ExecuteGetFD sends a file descriptor via SCM rights and assigns it a name +func (q *QMP) ExecuteGetFD(ctx context.Context, fdname string, fd *os.File) error { + oob := syscall.UnixRights(int(fd.Fd())) + args := map[string]interface{}{ + "fdname": fdname, + } + + _, err := q.executeCommandWithResponse(ctx, "getfd", args, oob, nil) + return err +} diff --git a/qemu/qmp_test.go b/qemu/qmp_test.go index 686064478..0ffdf5bc3 100644 --- a/qemu/qmp_test.go +++ b/qemu/qmp_test.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "log" + "os" "reflect" "sync" "testing" @@ -1015,7 +1016,24 @@ func TestExecutePCIVSockAdd(t *testing.T) { cfg := QMPConfig{Logger: qmpTestLogger{}} q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh) checkVersion(t, connectedCh) - err := q.ExecutePCIVSockAdd(context.Background(), "vsock-pci0", "3") + err := q.ExecutePCIVSockAdd(context.Background(), "vsock-pci0", "3", "1", true) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + q.Shutdown() + <-disconnectedCh +} + +// Checks getfd +func TestExecuteGetFdD(t *testing.T) { + connectedCh := make(chan *QMPVersion) + disconnectedCh := make(chan struct{}) + buf := newQMPTestCommandBuffer(t) + buf.AddCommand("getfd", nil, "return", nil) + cfg := QMPConfig{Logger: qmpTestLogger{}} + q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh) + checkVersion(t, connectedCh) + err := q.ExecuteGetFD(context.Background(), "foo", os.NewFile(0, "foo")) if err != nil { t.Fatalf("Unexpected error %v", err) }