@@ -81,6 +81,19 @@ func QmpExecDeviceAdd(socket, id string, busnum, devnum uint16) error {
81
81
qmpString := fmt .Sprintf (`{ "execute": "device_add", "arguments": { "driver": "usb-host", "hostbus": %d, "hostaddr": %d, "id": "%s"} }` , busnum , devnum , id )
82
82
_ , err := execRawCmd (socket , qmpString )
83
83
return err
84
+
85
+ // There is errors.Join(), but stupid Yetus has old golang
86
+ // and complains with "Join not declared by package errors".
87
+ // Use our own.
88
+ func joinErrors (err1 , err2 error ) error {
89
+ if err1 == nil {
90
+ return err2
91
+ }
92
+ if err2 == nil {
93
+ return err1
94
+ }
95
+
96
+ return fmt .Errorf ("%v; %v" , err1 , err2 )
84
97
}
85
98
86
99
func getQemuStatus (socket string ) (types.SwState , error ) {
@@ -102,7 +115,21 @@ func getQemuStatus(socket string) (types.SwState, error) {
102
115
"preconfig" : types .PAUSED ,
103
116
}
104
117
105
- if raw , err := execRawCmd (socket , `{ "execute": "query-status" }` ); err == nil {
118
+ // We do several retries, because correct QEMU status is very crucial to EVE
119
+ // and if for some reason (https://github.com/digitalocean/go-qemu/pull/210)
120
+ // the status is unexpected, EVE stops QEMU and game over.
121
+ var errs error
122
+ state := types .UNKNOWN
123
+ for attempt := 1 ; attempt <= 3 ; attempt ++ {
124
+ raw , err := execRawCmd (socket , `{ "execute": "query-status" }` )
125
+ if err != nil {
126
+ err = fmt .Errorf ("[attempt %d] qmp status failed for QMP socket '%s': err: '%v'; (JSON response: '%s')" ,
127
+ attempt , socket , err , raw )
128
+ errs = joinErrors (errs , err )
129
+ time .Sleep (time .Second )
130
+ continue
131
+ }
132
+
106
133
var result struct {
107
134
ID string `json:"id"`
108
135
Return struct {
@@ -114,18 +141,33 @@ func getQemuStatus(socket string) (types.SwState, error) {
114
141
dec := json .NewDecoder (bytes .NewReader (raw ))
115
142
dec .DisallowUnknownFields ()
116
143
err = dec .Decode (& result )
117
- var matched bool
118
- var state types.SwState
119
144
if err != nil {
120
- err = fmt .Errorf ("%v; (JSON received: '%s')" , err , raw )
121
- } else if state , matched = qmpStatusMap [result .Return .Status ]; ! matched {
122
- err = fmt .Errorf ("unknown QMP status '%s' for QMP socket '%s'; (JSON response: '%s')" ,
123
- result .Return .Status , socket , raw )
145
+ err = fmt .Errorf ("[attempt %d] failed to parse QMP status response for QMP socket '%s': err: '%v'; (JSON response: '%s')" ,
146
+ attempt , socket , err , raw )
147
+ errs = joinErrors (errs , err )
148
+ time .Sleep (time .Second )
149
+ continue
150
+ }
151
+ var matched bool
152
+ if state , matched = qmpStatusMap [result .Return .Status ]; ! matched {
153
+ err = fmt .Errorf ("[attempt %d] unknown QMP status '%s' for QMP socket '%s'; (JSON response: '%s')" ,
154
+ attempt , result .Return .Status , socket , raw )
155
+ errs = joinErrors (errs , err )
156
+ time .Sleep (time .Second )
157
+ continue
124
158
}
125
- return state , err
126
- } else {
127
- return types .UNKNOWN , err
159
+
160
+ if errs != nil {
161
+ logrus .Errorf ("getQemuStatus: %d retrieving status attempts failed '%v', but eventually '%s' status was retrieved, so return SUCCESS and continue" ,
162
+ attempt , errs , result .Return .Status )
163
+ errs = nil
164
+ }
165
+
166
+ // Success
167
+ break
128
168
}
169
+
170
+ return state , errs
129
171
}
130
172
131
173
func qmpEventHandler (listenerSocket , executorSocket string ) {
0 commit comments