/* * This file is part of Cockpit. * * Copyright (C) 2018 Red Hat, Inc. * * Cockpit is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * Cockpit is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Cockpit; If not, see . */ import cockpit from "cockpit"; const _ = cockpit.gettext; export const cpu_ram_info = () => cockpit.spawn(["cat", "/proc/meminfo", "/proc/cpuinfo"]) .then(text => { const info = { }; const memtotal_match = text.match(/MemTotal:[^0-9]*([0-9]+) [kK]B/); const total_kb = memtotal_match && parseInt(memtotal_match[1], 10); if (total_kb) info.memory = total_kb * 1024; const available_match = text.match(/MemAvailable:[^0-9]*([0-9]+) [kK]B/); const available_kb = available_match && parseInt(available_match[1], 10); if (available_kb) info.available_memory = available_kb * 1024; const swap_match = text.match(/SwapTotal:[^0-9]*([0-9]+) [kK]B/); const swap_total_kb = swap_match && parseInt(swap_match[1], 10); if (swap_total_kb) info.swap = swap_total_kb * 1024; let model_match = text.match(/^model name\s*:\s*(.*)$/m); if (!model_match) model_match = text.match(/^cpu\s*:\s*(.*)$/m); // PowerPC if (!model_match) model_match = text.match(/^vendor_id\s*:\s*(.*)$/m); // s390x if (model_match) info.cpu_model = model_match[1]; info.cpus = 0; const re = /^(processor|cpu number)\s*:/gm; while (re.test(text)) info.cpus += 1; return info; }); // https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf const chassis_types = [ undefined, _("Other"), _("Unknown"), _("Desktop"), _("Low profile desktop"), _("Pizza box"), _("Mini tower"), _("Tower"), _("Portable"), _("Laptop"), _("Notebook"), _("Handheld"), _("Docking station"), _("All-in-one"), _("Sub-Notebook"), _("Space-saving computer"), _("Lunch box"), /* 0x10 */ _("Main server chassis"), _("Expansion chassis"), _("Sub-Chassis"), _("Bus expansion chassis"), _("Peripheral chassis"), _("RAID chassis"), _("Rack mount chassis"), _("Sealed-case PC"), _("Multi-system chassis"), _("Compact PCI"), /* 0x1A */ _("Advanced TCA"), _("Blade"), _("Blade enclosure"), _("Tablet"), _("Convertible"), _("Detachable"), /* 0x20 */ _("IoT gateway"), _("Embedded PC"), _("Mini PC"), _("Stick PC"), ]; function parseDMIFields(text) { const info = {}; text.split("\n").forEach(line => { const sep = line.indexOf(':'); if (sep <= 0) return; const file = line.slice(0, sep); const key = file.slice(file.lastIndexOf('/') + 1); let value = line.slice(sep + 1); // clean up after lazy OEMs if (value.match(/to be filled by o\.?e\.?m\.?/i)) value = ""; info[key] = value; if (key === "chassis_type") info[key + "_str"] = chassis_types[parseInt(value)] || chassis_types[2]; // fall back to "Unknown" }); return info; } export function dmi_info() { // the grep often/usually exits with 2, that's okay as long as we find *some* information return cockpit.script("grep -r . /sys/class/dmi/id || true", null, { err: "message", superuser: "try" }) .then((output) => parseDMIFields(output)); } // decode a binary Uint8Array with a trailing null byte function decode_proc_str(s) { return cockpit.utf8_decoder().decode(s.slice(0, -1)); } export function devicetree_info() { let model, serial; return Promise.all([ // these succeed with content === null if files are absent cockpit.file("/proc/device-tree/model", { binary: true }).read() .then(content => { model = content ? decode_proc_str(content) : null }), cockpit.file("/proc/device-tree/serial-number", { binary: true }).read() .then(content => { serial = content ? decode_proc_str(content) : null }), ]) .then(() => ({ model, serial })); } /* we expect udev db paragraphs like this: * P: /devices/virtual/mem/null N: null E: DEVMODE=0666 E: DEVNAME=/dev/null E: SUBSYSTEM=mem */ const udevPathRE = /^P: (.*)$/; const udevPropertyRE = /^E: (\w+)=(.*)$/; function parseUdevDB(text) { const info = {}; text.split("\n\n").forEach(paragraph => { let syspath = null; const props = {}; paragraph = paragraph.trim(); if (!paragraph) return; paragraph.split("\n").forEach(line => { let match = line.match(udevPathRE); if (match) { syspath = match[1]; } else { match = line.match(udevPropertyRE); if (match) props[match[1]] = match[2]; } }); if (syspath) info[syspath] = props; else console.log("udev database paragraph is missing P:", paragraph); }); return info; } export function udev_info() { return cockpit.spawn(["udevadm", "info", "--export-db"], { err: "message" }) .then(output => parseUdevDB(output)); } const memoryRE = /^([ \w]+): (.*)/; // Process the dmidecode output and create a mapping of locator to DIMM properties function parseMemoryInfo(text) { const info = {}; text.split("\n\n").forEach(paragraph => { let locator = null; let bankLocator = null; const props = {}; paragraph = paragraph.trim(); if (!paragraph) return; paragraph.split("\n").forEach(line => { line = line.trim(); const match = line.match(memoryRE); if (match) props[match[1]] = match[2]; }); locator = props.Locator; bankLocator = props['Bank Locator']; if (locator) info[bankLocator + locator] = props; }); return processMemory(info); } // Select the useful properties to display function processMemory(info) { const memoryArray = []; for (const dimm in info) { const memoryProperty = info[dimm]; let memorySize = memoryProperty.Size || _("Unknown"); if (memorySize.includes("MB")) { const memorySizeValue = parseInt(memorySize, 10); memorySize = cockpit.format(_("$0 GiB"), memorySizeValue / 1024); } let memoryTechnology = memoryProperty["Memory technology"]; if (!memoryTechnology || memoryTechnology == "") memoryTechnology = _("Unknown"); let memoryRank = memoryProperty.Rank || _("Unknown"); if (memoryRank == 1) memoryRank = _("Single rank"); if (memoryRank == 2) memoryRank = _("Dual rank"); memoryArray.push({ locator: (memoryProperty['Bank Locator'] + ': ' + memoryProperty.Locator) || _("Unknown"), technology: memoryTechnology, type: memoryProperty.Type || _("Unknown"), size: memorySize, state: memoryProperty["Total Width"] == "Unknown" ? _("Absent") : _("Present"), rank: memoryRank, speed: memoryProperty.Speed || _("Unknown") }); } return memoryArray; } export function memory_info() { return cockpit.spawn(["dmidecode", "-t", "memory"], { environ: ["LC_ALL=C"], err: "message", superuser: "try" }) .then(output => parseMemoryInfo(output)); }