From c7fc7aacf838e8867890d16e77cfaa44bb62f700 Mon Sep 17 00:00:00 2001 From: Felipe Lalanne Date: Thu, 6 Jan 2022 20:32:50 +0000 Subject: [PATCH] Use dmidecode to read cpuid in non ARM devices Cpu id is set to null so far for non ARM devices (e.g. Intel NUC). This parses the output of dmidecode to get the cpu id and system model. Change-type: patch --- Dockerfile.template | 2 + src/lib/system-info.ts | 79 ++++++++++++++++++++++++++++++-- test/src/lib/system-info.spec.ts | 2 +- 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/Dockerfile.template b/Dockerfile.template index b81ee5c5..da8d58ae 100644 --- a/Dockerfile.template +++ b/Dockerfile.template @@ -33,6 +33,7 @@ RUN apk add --no-cache \ libuv \ sqlite-libs \ sqlite-dev \ + dmidecode \ dbus-dev COPY build-utils/node-sums.txt . @@ -99,6 +100,7 @@ RUN apk add --no-cache \ avahi \ dbus \ libstdc++ \ + dmidecode \ sqlite-libs WORKDIR /usr/src/app diff --git a/src/lib/system-info.ts b/src/lib/system-info.ts index b5808a74..8088fbcd 100644 --- a/src/lib/system-info.ts +++ b/src/lib/system-info.ts @@ -67,16 +67,89 @@ export async function getCpuTemp(): Promise { return Math.round(tempInfo.main); } -export async function getCpuId(): Promise { +export async function getSystemId(): Promise { try { + // This will work on arm devices const buffer = await fs.readFile('/proc/device-tree/serial-number'); // Remove the null byte at the end return buffer.toString('utf-8').replace(/\0/g, ''); } catch { - return undefined; + // Otherwise use dmidecode + const [baseBoardInfo] = (await dmidecode('baseboard')).filter( + (entry) => entry.type === 'Base Board Information', + ); + return baseBoardInfo?.values?.['Serial Number'] || undefined; } } +export async function getSystemModel(): Promise { + try { + const buffer = await fs.readFile('/proc/device-tree/model'); + // Remove the null byte at the end + return buffer.toString('utf-8').replace(/\0/g, ''); + } catch { + const [baseBoardInfo] = (await dmidecode('baseboard')).filter( + (entry) => entry.type === 'Base Board Information', + ); + + // Join manufacturer and product name in a single string + return ( + [ + baseBoardInfo?.values?.['Manufacturer'], + baseBoardInfo?.values?.['Product Name'], + ] + .filter((s) => !!s) + .join(' ') || undefined + ); + } +} + +/** + * Parse the output of dmidecode and return an array of + * objects {type: string, values: string[]} + * + * This only parses simple key,value pairs from the output + * of dmidecode, multiline strings and arrays are ignored + */ +export async function dmidecode(t: string) { + const { stdout: info } = await exec(`dmidecode -t ${t}`); + return ( + info + .toString() + .split(/\r?\n/) // Split by line jumps + // Split into groups by looking for empty lines + .reduce((groups, line) => { + const currentGroup = groups.pop() || []; + if (/^\s*$/.test(line)) { + // For each empty line create a new group + groups.push(currentGroup); + groups.push([]); + } else { + // Otherwise append the line to the group + currentGroup.push(line); + groups.push(currentGroup); + } + return groups; + }, [] as string[][]) + // Only select the handles + .filter((group) => group.length > 1 && /^Handle/.test(group[0])) + .map(([, type, ...lines]) => ({ + type, + values: lines + // Only select lines that match 'key: value', this will exclude multiline strings + // and arrays (we don't care about those for these purposes) + .filter((line) => /^\s+[^:]+: .+$/.test(line)) + .map((line) => { + const [key, value] = line.split(':').map((s) => s.trim()); + // Finally convert the lines into key value pairs + return { [key]: value }; + }) + // And merge + .reduce((vals, v) => ({ ...vals, ...v }), {}), + })) + ); +} + const undervoltageRegex = /under.*voltage/i; export async function undervoltageDetected(): Promise { try { @@ -110,7 +183,7 @@ export async function getSystemMetrics() { getCpuUsage(), getMemoryInformation(), getCpuTemp(), - getCpuId(), + getSystemId(), getStorageInfo(), ]); diff --git a/test/src/lib/system-info.spec.ts b/test/src/lib/system-info.spec.ts index c01e3054..dbf8b388 100644 --- a/test/src/lib/system-info.spec.ts +++ b/test/src/lib/system-info.spec.ts @@ -97,7 +97,7 @@ describe('System information', () => { }); it('gets CPU ID', async () => { - const cpuId = await sysInfo.getCpuId(); + const cpuId = await sysInfo.getSystemId(); expect(cpuId).to.equal('1000000001b93f3f'); }); });