qemu: fix qmp disconnection semi properly

this is actually something i need to push to crusttest as well, because
this will affect it as well, though not as badly because it will only break certain buttons
This commit is contained in:
modeco80
2024-05-22 17:56:04 -04:00
parent 2e05504e4a
commit e184bfb085
2 changed files with 50 additions and 50 deletions

View File

@@ -207,21 +207,26 @@ export class QemuVM extends EventEmitter {
if (!this.qmpConnected) { if (!this.qmpConnected) {
self.qmpInstance = new QmpClient(); self.qmpInstance = new QmpClient();
self.qmpInstance.on('close', async () => { let onQmpError = async (err: Error|undefined) => {
self.qmpConnected = false; self.qmpConnected = false;
// If we aren't stopping, then we do actually need to care QMP disconnected // If we aren't stopping, then we do actually need to care QMP disconnected
if (self.state != VMState.Stopping) { if (self.state != VMState.Stopping) {
//if(err !== undefined) // This doesn't show anything useful or maybe I'm just stupid idk
// self.VMLog().Error(`Error: ${err!}`)
if (self.qmpFailCount++ < kMaxFailCount) { if (self.qmpFailCount++ < kMaxFailCount) {
this.VMLog().Error(`Failed to connect to QMP ${self.qmpFailCount} times`); self.VMLog().Error(`Failed to connect to QMP ${self.qmpFailCount} times.`);
await Shared.Sleep(500); await Shared.Sleep(500);
await self.ConnectQmp(); await self.ConnectQmp();
} else { } else {
this.VMLog().Error(`Failed to connect to QMP ${self.qmpFailCount} times, giving up`); self.VMLog().Error(`Reached max retries, giving up.`);
await self.Stop(); await self.Stop();
} }
} }
}); };
self.qmpInstance.on('close', onQmpError);
self.qmpInstance.on('error', onQmpError);
self.qmpInstance.on('event', async (ev) => { self.qmpInstance.on('event', async (ev) => {
switch (ev.event) { switch (ev.event) {

View File

@@ -22,6 +22,12 @@ export default class QmpClient extends Socket {
private commandEntries: QmpCommandEntry[] = []; private commandEntries: QmpCommandEntry[] = [];
private lastID = 0; private lastID = 0;
constructor() {
super();
this.assignHandlers();
}
private ExecuteSync(command: string, args: any | null, callback: QmpCallback | null) { private ExecuteSync(command: string, args: any | null, callback: QmpCallback | null) {
let cmd: QmpCommandEntry = { let cmd: QmpCommandEntry = {
callback: callback, callback: callback,
@@ -65,71 +71,60 @@ export default class QmpClient extends Socket {
} }
// this can probably be made async // this can probably be made async
private ConnectImpl() { private assignHandlers() {
let self = this; let self = this;
this.once('connect', () => { this.on('connect', () => {
this.removeAllListeners('error'); // this should be more correct?
}); this.once('data', (data) => {
// Handshake QMP with the server.
self.qmpHandshakeData = JSON.parse(data.toString('utf8')).QMP;
self.Handshake(() => {
// Now ready to parse QMP responses/events.
self.pipe(split(JSON.parse))
.on('data', (json: any) => {
if (json == null) return self.end();
this.once('error', (err) => { if (json.return || json.error) {
// just rethrow lol // Our handshake has a spurious return because we never assign it an ID,
//throw err; // and it is gathered by this pipe for some reason I'm not quite sure about.
// So, just for safety's sake, don't process any return objects which don't have an ID attached to them.
if (json.id == null) return;
console.log('you have pants: rules,', err); let callbackEntry = this.commandEntries.find((entry) => entry.id === json.id);
}); let error: Error | null = json.error ? new Error(json.error.desc) : null;
this.once('data', (data) => { // we somehow didn't find a callback entry for this response.
// Handshake QMP with the server. // I don't know how. Techinically not an error..., but I guess you're not getting a reponse to whatever causes this to happen
self.qmpHandshakeData = JSON.parse(data.toString('utf8')).QMP; if (callbackEntry == null) return;
self.Handshake(() => {
// Now ready to parse QMP responses/events.
self.pipe(split(JSON.parse))
.on('data', (json: any) => {
if (json == null) return self.end();
if (json.return || json.error) { if (callbackEntry?.callback) callbackEntry.callback(error, json.return);
// Our handshake has a spurious return because we never assign it an ID,
// and it is gathered by this pipe for some reason I'm not quite sure about.
// So, just for safety's sake, don't process any return objects which don't have an ID attached to them.
if (json.id == null) return;
let callbackEntry = this.commandEntries.find((entry) => entry.id === json.id); // Remove the completed callback entry.
let error: Error | null = json.error ? new Error(json.error.desc) : null; this.commandEntries.slice(this.commandEntries.indexOf(callbackEntry));
} else if (json.event) {
// we somehow didn't find a callback entry for this response. this.emit('event', json);
// I don't know how. Techinically not an error..., but I guess you're not getting a reponse to whatever causes this to happen }
if (callbackEntry == null) return; })
.on('error', () => {
if (callbackEntry?.callback) callbackEntry.callback(error, json.return); // Give up.
return self.end();
// Remove the completed callback entry. });
this.commandEntries.slice(this.commandEntries.indexOf(callbackEntry)); this.emit('qmp-ready');
} else if (json.event) { });
this.emit('event', json);
}
})
.on('error', () => {
// Give up.
return self.end();
});
this.emit('qmp-ready');
}); });
}); });
this.once('close', () => { this.on('close', () => {
this.end(); this.end();
this.removeAllListeners('data'); // wow. good job bud. cool memory leak
}); });
} }
Connect(host: string, port: number) { Connect(host: string, port: number) {
super.connect(port, host); super.connect(port, host);
this.ConnectImpl();
} }
ConnectUNIX(path: string) { ConnectUNIX(path: string) {
super.connect(path); super.connect(path);
this.ConnectImpl();
} }
} }