From e184bfb085f67b563f5825a6b01699356c790a80 Mon Sep 17 00:00:00 2001 From: modeco80 Date: Wed, 22 May 2024 17:56:04 -0400 Subject: [PATCH] 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 --- qemu/src/QemuVM.ts | 13 +++++-- qemu/src/QmpClient.ts | 87 ++++++++++++++++++++----------------------- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/qemu/src/QemuVM.ts b/qemu/src/QemuVM.ts index 243fb94..ca1e396 100644 --- a/qemu/src/QemuVM.ts +++ b/qemu/src/QemuVM.ts @@ -207,21 +207,26 @@ export class QemuVM extends EventEmitter { if (!this.qmpConnected) { self.qmpInstance = new QmpClient(); - self.qmpInstance.on('close', async () => { + let onQmpError = async (err: Error|undefined) => { self.qmpConnected = false; // If we aren't stopping, then we do actually need to care QMP disconnected 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) { - 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 self.ConnectQmp(); } 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(); } } - }); + }; + + self.qmpInstance.on('close', onQmpError); + self.qmpInstance.on('error', onQmpError); self.qmpInstance.on('event', async (ev) => { switch (ev.event) { diff --git a/qemu/src/QmpClient.ts b/qemu/src/QmpClient.ts index 3cfda5e..fe93d68 100644 --- a/qemu/src/QmpClient.ts +++ b/qemu/src/QmpClient.ts @@ -22,6 +22,12 @@ export default class QmpClient extends Socket { private commandEntries: QmpCommandEntry[] = []; private lastID = 0; + constructor() { + super(); + + this.assignHandlers(); + } + private ExecuteSync(command: string, args: any | null, callback: QmpCallback | null) { let cmd: QmpCommandEntry = { callback: callback, @@ -65,71 +71,60 @@ export default class QmpClient extends Socket { } // this can probably be made async - private ConnectImpl() { + private assignHandlers() { let self = this; - this.once('connect', () => { - this.removeAllListeners('error'); - }); + this.on('connect', () => { + // 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) => { - // just rethrow lol - //throw err; + if (json.return || json.error) { + // 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; - 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) => { - // 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(); + // we somehow didn't find a callback entry for this response. + // 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; - if (json.return || json.error) { - // 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; + if (callbackEntry?.callback) callbackEntry.callback(error, json.return); - let callbackEntry = this.commandEntries.find((entry) => entry.id === json.id); - let error: Error | null = json.error ? new Error(json.error.desc) : null; - - // we somehow didn't find a callback entry for this response. - // 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; - - if (callbackEntry?.callback) callbackEntry.callback(error, json.return); - - // Remove the completed callback entry. - this.commandEntries.slice(this.commandEntries.indexOf(callbackEntry)); - } else if (json.event) { - this.emit('event', json); - } - }) - .on('error', () => { - // Give up. - return self.end(); - }); - this.emit('qmp-ready'); + // Remove the completed callback entry. + this.commandEntries.slice(this.commandEntries.indexOf(callbackEntry)); + } 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.removeAllListeners('data'); // wow. good job bud. cool memory leak }); } Connect(host: string, port: number) { super.connect(port, host); - this.ConnectImpl(); } ConnectUNIX(path: string) { super.connect(path); - this.ConnectImpl(); } }