github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/vm/qemu/qmp.go (about) 1 // Copyright 2020 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package qemu 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "net" 10 11 "github.com/google/syzkaller/pkg/log" 12 ) 13 14 type qmpVersion struct { 15 Package string 16 QEMU struct { 17 Major int 18 Micro int 19 Minor int 20 } 21 } 22 23 type qmpBanner struct { 24 QMP struct { 25 Version qmpVersion 26 } 27 } 28 29 type qmpCommand struct { 30 Execute string `json:"execute"` 31 Arguments interface{} `json:"arguments,omitempty"` 32 } 33 34 type hmpCommand struct { 35 Command string `json:"command-line"` 36 CPU int `json:"cpu-index"` 37 } 38 39 type qmpResponse struct { 40 Error struct { 41 Class string 42 Desc string 43 } 44 Return interface{} 45 46 Event string 47 Data map[string]interface{} 48 Timestamp struct { 49 Seconds int64 50 Microseconds int64 51 } 52 } 53 54 func (inst *instance) qmpConnCheck() error { 55 if inst.mon != nil { 56 return nil 57 } 58 59 addr := fmt.Sprintf("127.0.0.1:%v", inst.monport) 60 conn, err := net.Dial("tcp", addr) 61 if err != nil { 62 return err 63 } 64 65 monDec := json.NewDecoder(conn) 66 monEnc := json.NewEncoder(conn) 67 68 var banner qmpBanner 69 if err := monDec.Decode(&banner); err != nil { 70 return err 71 } 72 73 inst.monEnc = monEnc 74 inst.monDec = monDec 75 if _, err := inst.doQmp(&qmpCommand{Execute: "qmp_capabilities"}); err != nil { 76 inst.monEnc = nil 77 inst.monDec = nil 78 return err 79 } 80 inst.mon = conn 81 82 return nil 83 } 84 85 func (inst *instance) qmpRecv() (*qmpResponse, error) { 86 for { 87 qmp := new(qmpResponse) 88 err := inst.monDec.Decode(qmp) 89 if err != nil || qmp.Event == "" { 90 return qmp, err 91 } 92 log.Logf(1, "event: %v", qmp) 93 } 94 } 95 96 func (inst *instance) doQmp(cmd *qmpCommand) (*qmpResponse, error) { 97 if err := inst.monEnc.Encode(cmd); err != nil { 98 return nil, err 99 } 100 return inst.qmpRecv() 101 } 102 103 func (inst *instance) qmp(cmd *qmpCommand) (interface{}, error) { 104 if err := inst.qmpConnCheck(); err != nil { 105 return nil, err 106 } 107 resp, err := inst.doQmp(cmd) 108 if err != nil { 109 return nil, err 110 } 111 if resp.Error.Desc != "" { 112 return nil, fmt.Errorf("error %v", resp.Error) 113 } 114 if resp.Return == nil { 115 return nil, fmt.Errorf(`no "return" nor "error" in [%v]`, resp) 116 } 117 return resp.Return, nil 118 } 119 120 func (inst *instance) hmp(cmd string, cpu int) (string, error) { 121 req := &qmpCommand{ 122 Execute: "human-monitor-command", 123 Arguments: &hmpCommand{ 124 Command: cmd, 125 CPU: cpu, 126 }, 127 } 128 resp, err := inst.qmp(req) 129 if err != nil { 130 return "", err 131 } 132 return resp.(string), nil 133 }