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
This commit is contained in:
Felipe Lalanne 2022-01-06 20:32:50 +00:00
parent fcaef150a8
commit c7fc7aacf8
3 changed files with 79 additions and 4 deletions

View File

@ -33,6 +33,7 @@ RUN apk add --no-cache \
libuv \ libuv \
sqlite-libs \ sqlite-libs \
sqlite-dev \ sqlite-dev \
dmidecode \
dbus-dev dbus-dev
COPY build-utils/node-sums.txt . COPY build-utils/node-sums.txt .
@ -99,6 +100,7 @@ RUN apk add --no-cache \
avahi \ avahi \
dbus \ dbus \
libstdc++ \ libstdc++ \
dmidecode \
sqlite-libs sqlite-libs
WORKDIR /usr/src/app WORKDIR /usr/src/app

View File

@ -67,16 +67,89 @@ export async function getCpuTemp(): Promise<number> {
return Math.round(tempInfo.main); return Math.round(tempInfo.main);
} }
export async function getCpuId(): Promise<string | undefined> { export async function getSystemId(): Promise<string | undefined> {
try { try {
// This will work on arm devices
const buffer = await fs.readFile('/proc/device-tree/serial-number'); const buffer = await fs.readFile('/proc/device-tree/serial-number');
// Remove the null byte at the end // Remove the null byte at the end
return buffer.toString('utf-8').replace(/\0/g, ''); return buffer.toString('utf-8').replace(/\0/g, '');
} catch { } 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<string | undefined> {
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; const undervoltageRegex = /under.*voltage/i;
export async function undervoltageDetected(): Promise<boolean> { export async function undervoltageDetected(): Promise<boolean> {
try { try {
@ -110,7 +183,7 @@ export async function getSystemMetrics() {
getCpuUsage(), getCpuUsage(),
getMemoryInformation(), getMemoryInformation(),
getCpuTemp(), getCpuTemp(),
getCpuId(), getSystemId(),
getStorageInfo(), getStorageInfo(),
]); ]);

View File

@ -97,7 +97,7 @@ describe('System information', () => {
}); });
it('gets CPU ID', async () => { it('gets CPU ID', async () => {
const cpuId = await sysInfo.getCpuId(); const cpuId = await sysInfo.getSystemId();
expect(cpuId).to.equal('1000000001b93f3f'); expect(cpuId).to.equal('1000000001b93f3f');
}); });
}); });