LCOV - code coverage report
Current view: top level - pkg/lib - machine-info.js Coverage Total Hit
Test: cockpit Lines: 98.5 % 194 191
Test Date: 2026-06-17 06:28:00

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2018 Red Hat, Inc.
       3              :  * SPDX-License-Identifier: LGPL-2.1-or-later
       4              :  */
       5              : 
       6           49 : import cockpit from "cockpit";
       7           49 : const _ = cockpit.gettext;
       8              : 
       9           49 : export const cpu_ram_info = () =>
      10           49 :     cockpit.spawn(["cat", "/proc/meminfo", "/proc/cpuinfo"])
      11           46 :             .then(text => {
      12           46 :                 const info = { };
      13           46 :                 const memtotal_match = text.match(/MemTotal:[^0-9]*([0-9]+) [kK]B/);
      14           46 :                 const total_kb = memtotal_match && parseInt(memtotal_match[1], 10);
      15           46 :                 if (total_kb)
      16           46 :                     info.memory = total_kb * 1024;
      17              : 
      18           46 :                 const available_match = text.match(/MemAvailable:[^0-9]*([0-9]+) [kK]B/);
      19           46 :                 const available_kb = available_match && parseInt(available_match[1], 10);
      20           46 :                 if (available_kb)
      21           46 :                     info.available_memory = available_kb * 1024;
      22              : 
      23           46 :                 const swap_match = text.match(/SwapTotal:[^0-9]*([0-9]+) [kK]B/);
      24           46 :                 const swap_total_kb = swap_match && parseInt(swap_match[1], 10);
      25           46 :                 if (swap_total_kb)
      26           46 :                     info.swap = swap_total_kb * 1024;
      27              : 
      28           46 :                 let model_match = text.match(/^model name\s*:\s*(.*)$/m);
      29           46 :                 if (!model_match)
      30            8 :                     model_match = text.match(/^cpu\s*:\s*(.*)$/m); // PowerPC
      31           46 :                 if (!model_match)
      32            8 :                     model_match = text.match(/^vendor_id\s*:\s*(.*)$/m); // s390x
      33           46 :                 if (!model_match)
      34            8 :                     model_match = text.match(/^Model Name\s*:\s*(.*)$/m); // LoongArch
      35           46 :                 if (model_match)
      36           46 :                     info.cpu_model = model_match[1];
      37              : 
      38           46 :                 info.cpus = 0;
      39           46 :                 const re = /^(processor|cpu number)\s*:/gm;
      40           46 :                 while (re.test(text))
      41           46 :                     info.cpus += 1;
      42           46 :                 return info;
      43           46 :             });
      44              : 
      45              : // https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
      46           49 : const chassis_types = [
      47           49 :     undefined,
      48           49 :     _("Other"),
      49           49 :     _("Unknown"),
      50           49 :     _("Desktop"),
      51           49 :     _("Low profile desktop"),
      52           49 :     _("Pizza box"),
      53           49 :     _("Mini tower"),
      54           49 :     _("Tower"),
      55           49 :     _("Portable"),
      56           49 :     _("Laptop"),
      57           49 :     _("Notebook"),
      58           49 :     _("Handheld"),
      59           49 :     _("Docking station"),
      60           49 :     _("All-in-one"),
      61           49 :     _("Sub-Notebook"),
      62           49 :     _("Space-saving computer"),
      63           49 :     _("Lunch box"), /* 0x10 */
      64           49 :     _("Main server chassis"),
      65           49 :     _("Expansion chassis"),
      66           49 :     _("Sub-Chassis"),
      67           49 :     _("Bus expansion chassis"),
      68           49 :     _("Peripheral chassis"),
      69           49 :     _("RAID chassis"),
      70           49 :     _("Rack mount chassis"),
      71           49 :     _("Sealed-case PC"),
      72           49 :     _("Multi-system chassis"),
      73           49 :     _("Compact PCI"), /* 0x1A */
      74           49 :     _("Advanced TCA"),
      75           49 :     _("Blade"),
      76           49 :     _("Blade enclosure"),
      77           49 :     _("Tablet"),
      78           49 :     _("Convertible"),
      79           49 :     _("Detachable"), /* 0x20 */
      80           49 :     _("IoT gateway"),
      81           49 :     _("Embedded PC"),
      82           49 :     _("Mini PC"),
      83           49 :     _("Stick PC"),
      84           49 : ];
      85              : 
      86           35 : function parseDMIFields(text) {
      87           35 :     const info = {};
      88           35 :     text.split("\n").forEach(line => {
      89           35 :         const sep = line.indexOf(':');
      90           35 :         if (sep <= 0)
      91           35 :             return;
      92           35 :         const file = line.slice(0, sep);
      93           35 :         const key = file.slice(file.lastIndexOf('/') + 1);
      94           35 :         let value = line.slice(sep + 1);
      95              : 
      96              :         // clean up after lazy OEMs
      97           35 :         if (value.match(/to be filled by o\.?e\.?m\.?/i))
      98            7 :             value = "";
      99              : 
     100           35 :         info[key] = value;
     101              : 
     102           35 :         if (key === "chassis_type")
     103            7 :             info[key + "_str"] = chassis_types[parseInt(value)] || chassis_types[2]; // fall back to "Unknown"
     104           35 :     });
     105           35 :     return info;
     106           35 : }
     107              : 
     108           38 : export function dmi_info() {
     109              :     // the grep often/usually exits with 2, that's okay as long as we find *some* information
     110           38 :     return cockpit.script("grep -r . /sys/class/dmi/id || true", null,
     111           38 :                           { err: "message", superuser: "try" })
     112           35 :             .then((output) => parseDMIFields(output));
     113           38 : }
     114              : 
     115              : // decode a binary Uint8Array with a trailing null byte
     116            0 : function decode_proc_str(s) {
     117            0 :     return new TextDecoder().decode(s.slice(0, -1));
     118            0 : }
     119              : 
     120            2 : export function devicetree_info() {
     121            2 :     let model;
     122            2 :     let serial;
     123              : 
     124            2 :     return Promise.all([
     125              :         // these succeed with content === null if files are absent
     126            2 :         cockpit.file("/proc/device-tree/model", { binary: true }).read()
     127            1 :                 .then(content => { model = content ? decode_proc_str(content) : null }),
     128            2 :         cockpit.file("/proc/device-tree/serial-number", { binary: true }).read()
     129            1 :                 .then(content => { serial = content ? decode_proc_str(content) : null }),
     130            2 :     ])
     131            2 :             .then(() => ({ model, serial }));
     132            2 : }
     133              : 
     134              : /* we expect udev db paragraphs like this:
     135              :  *
     136              :    P: /devices/virtual/mem/null
     137              :    N: null
     138              :    E: DEVMODE=0666
     139              :    E: DEVNAME=/dev/null
     140              :    E: SUBSYSTEM=mem
     141              : */
     142              : 
     143            2 : const udevPathRE = /^P: (.*)$/;
     144            2 : const udevPropertyRE = /^E: (\w+)=(.*)$/;
     145              : 
     146            2 : function parseUdevDB(text) {
     147            2 :     const info = {};
     148            2 :     text.split("\n\n").forEach(paragraph => {
     149            2 :         let syspath = null;
     150            2 :         const props = {};
     151              : 
     152            2 :         paragraph = paragraph.trim();
     153            2 :         if (!paragraph)
     154            2 :             return;
     155              : 
     156            2 :         paragraph.split("\n").forEach(line => {
     157            2 :             let match = line.match(udevPathRE);
     158            2 :             if (match) {
     159            2 :                 syspath = match[1];
     160            2 :             } else {
     161            2 :                 match = line.match(udevPropertyRE);
     162            2 :                 if (match)
     163            2 :                     props[match[1]] = match[2];
     164            2 :             }
     165            2 :         });
     166              : 
     167            2 :         if (syspath)
     168            1 :             info[syspath] = props;
     169              :         else
     170            1 :             console.log("udev database paragraph is missing P:", paragraph);
     171            2 :     });
     172            2 :     return info;
     173            2 : }
     174              : 
     175            2 : export function udev_info() {
     176            2 :     return cockpit.spawn(["udevadm", "info", "--export-db"], { err: "message" })
     177            2 :             .then(output => parseUdevDB(output));
     178            2 : }
     179              : 
     180            2 : const memoryRE = /^([ \w]+): (.*)/;
     181              : 
     182              : // Process the dmidecode output and create a mapping of locator to DIMM properties
     183            2 : function parseMemoryInfo(text) {
     184            2 :     const info = {};
     185            2 :     text.split("\n\n").forEach(paragraph => {
     186            2 :         let locator = null;
     187            2 :         let bankLocator = null;
     188            2 :         const props = {};
     189            2 :         paragraph = paragraph.trim();
     190            2 :         if (!paragraph)
     191            2 :             return;
     192              : 
     193            2 :         paragraph.split("\n").forEach(line => {
     194            2 :             line = line.trim();
     195            2 :             const match = line.match(memoryRE);
     196            2 :             if (match)
     197            2 :                 props[match[1]] = match[2];
     198            2 :         });
     199              : 
     200            2 :         locator = props.Locator;
     201            2 :         bankLocator = props['Bank Locator'];
     202            2 :         if (locator)
     203            2 :             info[bankLocator + locator] = props;
     204            2 :     });
     205            2 :     return processMemory(info);
     206            2 : }
     207              : 
     208              : // Select the useful properties to display
     209            2 : function processMemory(info) {
     210            2 :     const memoryArray = [];
     211              : 
     212            2 :     for (const dimm in info) {
     213            2 :         const memoryProperty = info[dimm];
     214              : 
     215            1 :         let memorySize = memoryProperty.Size || _("Unknown");
     216            2 :         if (memorySize.includes("MB")) {
     217            2 :             const memorySizeValue = parseInt(memorySize, 10);
     218            2 :             memorySize = cockpit.format(_("$0 GiB"), memorySizeValue / 1024);
     219            2 :         }
     220              : 
     221            2 :         let memoryTechnology = memoryProperty["Memory technology"];
     222            1 :         if (!memoryTechnology || memoryTechnology == "<OUT OF SPEC>")
     223            2 :             memoryTechnology = _("Unknown");
     224              : 
     225            1 :         let memoryRank = memoryProperty.Rank || _("Unknown");
     226            2 :         if (memoryRank == 1)
     227            1 :             memoryRank = _("Single rank");
     228            2 :         if (memoryRank == 2)
     229            1 :             memoryRank = _("Dual rank");
     230              : 
     231            2 :         memoryArray.push({
     232            1 :             locator: (memoryProperty['Bank Locator'] + ': ' + memoryProperty.Locator) || _("Unknown"),
     233            2 :             technology: memoryTechnology,
     234            1 :             type: memoryProperty.Type || _("Unknown"),
     235            2 :             size: memorySize,
     236            1 :             state: memoryProperty["Total Width"] == "Unknown" ? _("Absent") : _("Present"),
     237            2 :             rank: memoryRank,
     238            1 :             speed: memoryProperty.Speed || _("Unknown")
     239            2 :         });
     240            2 :     }
     241              : 
     242            2 :     return memoryArray;
     243            2 : }
     244              : 
     245            2 : export function memory_info() {
     246            2 :     return cockpit.spawn(["dmidecode", "-t", "memory"], { environ: ["LC_ALL=C"], err: "message", superuser: "try" })
     247            2 :             .then(output => parseMemoryInfo(output));
     248            2 : }
        

Generated by: LCOV version 2.0-1