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-25 09:20:42

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2018 Red Hat, Inc.
       3              :  * SPDX-License-Identifier: LGPL-2.1-or-later
       4              :  */
       5              : 
       6          108 : import cockpit from "cockpit";
       7          108 : const _ = cockpit.gettext;
       8              : 
       9          108 : export const cpu_ram_info = () =>
      10          108 :     cockpit.spawn(["cat", "/proc/meminfo", "/proc/cpuinfo"])
      11           99 :             .then(text => {
      12           99 :                 const info = { };
      13           99 :                 const memtotal_match = text.match(/MemTotal:[^0-9]*([0-9]+) [kK]B/);
      14           99 :                 const total_kb = memtotal_match && parseInt(memtotal_match[1], 10);
      15           99 :                 if (total_kb)
      16           99 :                     info.memory = total_kb * 1024;
      17              : 
      18           99 :                 const available_match = text.match(/MemAvailable:[^0-9]*([0-9]+) [kK]B/);
      19           99 :                 const available_kb = available_match && parseInt(available_match[1], 10);
      20           99 :                 if (available_kb)
      21           99 :                     info.available_memory = available_kb * 1024;
      22              : 
      23           99 :                 const swap_match = text.match(/SwapTotal:[^0-9]*([0-9]+) [kK]B/);
      24           99 :                 const swap_total_kb = swap_match && parseInt(swap_match[1], 10);
      25           99 :                 if (swap_total_kb)
      26           99 :                     info.swap = swap_total_kb * 1024;
      27              : 
      28           99 :                 let model_match = text.match(/^model name\s*:\s*(.*)$/m);
      29           99 :                 if (!model_match)
      30           19 :                     model_match = text.match(/^cpu\s*:\s*(.*)$/m); // PowerPC
      31           99 :                 if (!model_match)
      32           19 :                     model_match = text.match(/^vendor_id\s*:\s*(.*)$/m); // s390x
      33           99 :                 if (!model_match)
      34           19 :                     model_match = text.match(/^Model Name\s*:\s*(.*)$/m); // LoongArch
      35           99 :                 if (model_match)
      36           99 :                     info.cpu_model = model_match[1];
      37              : 
      38           99 :                 info.cpus = 0;
      39           99 :                 const re = /^(processor|cpu number)\s*:/gm;
      40           99 :                 while (re.test(text))
      41           99 :                     info.cpus += 1;
      42           99 :                 return info;
      43           99 :             });
      44              : 
      45              : // https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
      46          108 : const chassis_types = [
      47          108 :     undefined,
      48          108 :     _("Other"),
      49          108 :     _("Unknown"),
      50          108 :     _("Desktop"),
      51          108 :     _("Low profile desktop"),
      52          108 :     _("Pizza box"),
      53          108 :     _("Mini tower"),
      54          108 :     _("Tower"),
      55          108 :     _("Portable"),
      56          108 :     _("Laptop"),
      57          108 :     _("Notebook"),
      58          108 :     _("Handheld"),
      59          108 :     _("Docking station"),
      60          108 :     _("All-in-one"),
      61          108 :     _("Sub-Notebook"),
      62          108 :     _("Space-saving computer"),
      63          108 :     _("Lunch box"), /* 0x10 */
      64          108 :     _("Main server chassis"),
      65          108 :     _("Expansion chassis"),
      66          108 :     _("Sub-Chassis"),
      67          108 :     _("Bus expansion chassis"),
      68          108 :     _("Peripheral chassis"),
      69          108 :     _("RAID chassis"),
      70          108 :     _("Rack mount chassis"),
      71          108 :     _("Sealed-case PC"),
      72          108 :     _("Multi-system chassis"),
      73          108 :     _("Compact PCI"), /* 0x1A */
      74          108 :     _("Advanced TCA"),
      75          108 :     _("Blade"),
      76          108 :     _("Blade enclosure"),
      77          108 :     _("Tablet"),
      78          108 :     _("Convertible"),
      79          108 :     _("Detachable"), /* 0x20 */
      80          108 :     _("IoT gateway"),
      81          108 :     _("Embedded PC"),
      82          108 :     _("Mini PC"),
      83          108 :     _("Stick PC"),
      84          108 : ];
      85              : 
      86           89 : function parseDMIFields(text) {
      87           89 :     const info = {};
      88           89 :     text.split("\n").forEach(line => {
      89           89 :         const sep = line.indexOf(':');
      90           89 :         if (sep <= 0)
      91           89 :             return;
      92           89 :         const file = line.slice(0, sep);
      93           89 :         const key = file.slice(file.lastIndexOf('/') + 1);
      94           89 :         let value = line.slice(sep + 1);
      95              : 
      96              :         // clean up after lazy OEMs
      97           89 :         if (value.match(/to be filled by o\.?e\.?m\.?/i))
      98           18 :             value = "";
      99              : 
     100           89 :         info[key] = value;
     101              : 
     102           89 :         if (key === "chassis_type")
     103           18 :             info[key + "_str"] = chassis_types[parseInt(value)] || chassis_types[2]; // fall back to "Unknown"
     104           89 :     });
     105           89 :     return info;
     106           89 : }
     107              : 
     108           97 : export function dmi_info() {
     109              :     // the grep often/usually exits with 2, that's okay as long as we find *some* information
     110           97 :     return cockpit.script("grep -r . /sys/class/dmi/id || true", null,
     111           97 :                           { err: "message", superuser: "try" })
     112           89 :             .then((output) => parseDMIFields(output));
     113           97 : }
     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