@depup/systeminformation 5.33.8-depup.0 → 5.33.12-depup.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/changes.json +1 -1
- package/lib/battery.js +32 -11
- package/lib/filesystem.js +101 -21
- package/lib/network.js +2 -0
- package/lib/osinfo.js +17 -3
- package/lib/processes.js +80 -43
- package/lib/util.js +7 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -13,8 +13,8 @@ npm install @depup/systeminformation
|
|
|
13
13
|
|
|
14
14
|
| Field | Value |
|
|
15
15
|
|-------|-------|
|
|
16
|
-
| Original | [systeminformation](https://www.npmjs.com/package/systeminformation) @ 5.33.
|
|
17
|
-
| Processed | 2026-09-
|
|
16
|
+
| Original | [systeminformation](https://www.npmjs.com/package/systeminformation) @ 5.33.12 |
|
|
17
|
+
| Processed | 2026-09-20 |
|
|
18
18
|
| Smoke test | passed |
|
|
19
19
|
| Deps updated | 0 |
|
|
20
20
|
|
package/changes.json
CHANGED
package/lib/battery.js
CHANGED
|
@@ -29,7 +29,7 @@ const _sunos = _platform === 'sunos';
|
|
|
29
29
|
|
|
30
30
|
function parseWinBatteryPart(lines, designedCapacity, fullChargeCapacity) {
|
|
31
31
|
const result = {};
|
|
32
|
-
|
|
32
|
+
const status = parseInt(util.getValue(lines, 'BatteryStatus', ':').trim(), 10) || 0;
|
|
33
33
|
// let status = util.getValue(lines, 'BatteryStatus', ':').trim();
|
|
34
34
|
// 1 = "Discharging"
|
|
35
35
|
// 2 = "On A/C"
|
|
@@ -111,7 +111,7 @@ module.exports = (callback) =>
|
|
|
111
111
|
if (battery_path) {
|
|
112
112
|
fs.readFile(battery_path + 'uevent', (error, stdout) => {
|
|
113
113
|
if (!error) {
|
|
114
|
-
|
|
114
|
+
const lines = stdout.toString().split('\n');
|
|
115
115
|
|
|
116
116
|
result.isCharging = util.getValue(lines, 'POWER_SUPPLY_STATUS', '=').toLowerCase() === 'charging';
|
|
117
117
|
result.acConnected = acConnected || result.isCharging;
|
|
@@ -176,7 +176,7 @@ module.exports = (callback) =>
|
|
|
176
176
|
}
|
|
177
177
|
if (_freebsd || _openbsd || _netbsd) {
|
|
178
178
|
exec('sysctl -i hw.acpi.battery hw.acpi.acline', (error, stdout) => {
|
|
179
|
-
|
|
179
|
+
const lines = stdout.toString().split('\n');
|
|
180
180
|
const batteries = parseInt('0' + util.getValue(lines, 'hw.acpi.battery.units'), 10);
|
|
181
181
|
const percent = parseInt('0' + util.getValue(lines, 'hw.acpi.battery.life'), 10);
|
|
182
182
|
result.hasBattery = batteries > 0;
|
|
@@ -196,24 +196,45 @@ module.exports = (callback) =>
|
|
|
196
196
|
|
|
197
197
|
if (_darwin) {
|
|
198
198
|
exec(
|
|
199
|
-
'ioreg -n AppleSmartBattery -r | egrep "CycleCount|IsCharging|DesignCapacity|MaxCapacity|CurrentCapacity|DeviceName|BatterySerialNumber|Serial|TimeRemaining|Voltage"; pmset -g batt | grep %',
|
|
199
|
+
'ioreg -n AppleSmartBattery -r | egrep "CycleCount|IsCharging|DesignCapacity|MaxCapacity|CurrentCapacity|DeviceName|BatterySerialNumber|Serial|TimeRemaining|Voltage|BatteryData|NominalChargeCapacity"; pmset -g batt | grep %',
|
|
200
200
|
(error, stdout) => {
|
|
201
201
|
if (stdout) {
|
|
202
|
-
|
|
202
|
+
const lines = stdout
|
|
203
|
+
.toString()
|
|
204
|
+
.replace(/^[ |]+/gm, '')
|
|
205
|
+
.replace(/ +/g, '')
|
|
206
|
+
.replace(/"+/g, '')
|
|
207
|
+
.replace(/-/g, '')
|
|
208
|
+
.split('\n');
|
|
209
|
+
const voltage = parseInt('0' + util.getValue(lines, 'voltage', '='), 10) / 1000.0;
|
|
210
|
+
const batteryData = util.getValue(lines, 'BatteryData', '=').replace(/^\{/, '').replace(/\}$/, '').split(',');
|
|
211
|
+
const maxCapacity = Math.round(
|
|
212
|
+
parseInt(
|
|
213
|
+
'0' +
|
|
214
|
+
(util.getValue(lines, 'AppleRawMaxCapacity', '=') ||
|
|
215
|
+
util.getValue(lines, 'NominalChargeCapacity', '=') ||
|
|
216
|
+
util.getValue(batteryData, 'FullChargeCapacity', '=') ||
|
|
217
|
+
util.getValue(batteryData, 'NominalChargeCapacity', '=')),
|
|
218
|
+
10
|
|
219
|
+
) * (voltage || 1)
|
|
220
|
+
);
|
|
221
|
+
const currentCapacity = Math.round(parseInt('0' + (util.getValue(lines, 'AppleRawCurrentCapacity', '=') || util.getValue(batteryData, 'RemainingCapacity', '=')), 10) * (voltage || 1));
|
|
222
|
+
const designedCapacity = Math.round(parseInt('0' + (util.getValue(lines, 'DesignCapacity', '=') || util.getValue(batteryData, 'DesignCapacity', '=')), 10) * (voltage || 1));
|
|
223
|
+
|
|
203
224
|
result.cycleCount = parseInt('0' + util.getValue(lines, 'cyclecount', '='), 10);
|
|
204
|
-
result.voltage =
|
|
225
|
+
result.voltage = voltage;
|
|
205
226
|
result.capacityUnit = result.voltage ? 'mWh' : 'mAh';
|
|
206
|
-
result.maxCapacity =
|
|
207
|
-
result.currentCapacity =
|
|
208
|
-
result.designedCapacity =
|
|
227
|
+
result.maxCapacity = maxCapacity;
|
|
228
|
+
result.currentCapacity = currentCapacity;
|
|
229
|
+
result.designedCapacity = designedCapacity;
|
|
209
230
|
result.manufacturer = 'Apple';
|
|
210
231
|
result.serial = util.getValue(lines, 'BatterySerialNumber', '=') || util.getValue(lines, 'Serial', '=');
|
|
211
232
|
result.model = util.getValue(lines, 'DeviceName', '=');
|
|
212
233
|
let percent = null;
|
|
213
234
|
const line = util.getValue(lines, 'internal', 'Battery');
|
|
214
|
-
|
|
235
|
+
const parts = line.split(';');
|
|
215
236
|
if (parts && parts[0]) {
|
|
216
|
-
|
|
237
|
+
const parts2 = parts[0].split('\t');
|
|
217
238
|
if (parts2 && parts2[1]) {
|
|
218
239
|
percent = parseFloat(parts2[1].trim().replace(/%/g, ''));
|
|
219
240
|
}
|
package/lib/filesystem.js
CHANGED
|
@@ -43,8 +43,20 @@ function fsSize(drive, callback) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
let macOsDisks = [];
|
|
46
|
+
const macOsFsTypes = new Map();
|
|
46
47
|
let osMounts = [];
|
|
47
48
|
|
|
49
|
+
// macOS df has no type column, so the type used to be guessed from diskutil - which only ever
|
|
50
|
+
// produced APFS, HFS or NFS and therefore never recognised zfs, exfat, msdos or smbfs. mount
|
|
51
|
+
// knows the real type, so prefer it and keep the old names for the three it could produce.
|
|
52
|
+
function macOsFsType(fs) {
|
|
53
|
+
const type = macOsFsTypes.get(fs);
|
|
54
|
+
if (!type) {
|
|
55
|
+
return getmacOsFsType(fs);
|
|
56
|
+
}
|
|
57
|
+
return type === 'apfs' ? 'APFS' : type === 'hfs' ? 'HFS' : type === 'nfs' ? 'NFS' : type;
|
|
58
|
+
}
|
|
59
|
+
|
|
48
60
|
function getmacOsFsType(fs) {
|
|
49
61
|
if (!fs.startsWith('/')) {
|
|
50
62
|
return 'NFS';
|
|
@@ -86,6 +98,59 @@ function fsSize(drive, callback) {
|
|
|
86
98
|
return lines;
|
|
87
99
|
}
|
|
88
100
|
|
|
101
|
+
// ZFS datasets share the pool, so df only reports what a dataset references itself - a parent
|
|
102
|
+
// holding its data in child datasets looks empty (#1017). Only `zfs list` knows the
|
|
103
|
+
// hierarchical usage, so query it once when a zfs mount is present.
|
|
104
|
+
function applyZfsUsage(data, cb) {
|
|
105
|
+
if (!data.some((item) => item.type === 'zfs')) {
|
|
106
|
+
return cb(data);
|
|
107
|
+
}
|
|
108
|
+
exec('zfs list -H -p -o name,used,avail,mountpoint', { ...util.execOptsLinux, timeout: 5000 }, (error, stdout) => {
|
|
109
|
+
if (error) {
|
|
110
|
+
// a truncated or timed out listing would correct only part of the datasets and leave the
|
|
111
|
+
// rest on their df values - correct none instead, so the result stays consistent
|
|
112
|
+
return cb(data);
|
|
113
|
+
}
|
|
114
|
+
const byMount = Object.create(null);
|
|
115
|
+
const byName = Object.create(null);
|
|
116
|
+
(stdout || '')
|
|
117
|
+
.toString()
|
|
118
|
+
.split('\n')
|
|
119
|
+
.forEach((line) => {
|
|
120
|
+
const parts = line.split('\t');
|
|
121
|
+
if (parts.length < 4) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const entry = { used: parseInt(parts[1], 10), available: parseInt(parts[2], 10) };
|
|
125
|
+
if (isNaN(entry.used) || isNaN(entry.available)) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
byName[parts[0]] = entry;
|
|
129
|
+
const mount = parts[3].trim();
|
|
130
|
+
// several datasets can carry the same mountpoint (root-on-zfs boot environments all
|
|
131
|
+
// declare "/"), so the mount index is only a fallback - keep the first one
|
|
132
|
+
if (mount.startsWith('/') && !byMount[mount]) {
|
|
133
|
+
byMount[mount] = entry;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
data.forEach((item) => {
|
|
137
|
+
if (item.type !== 'zfs') {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
// the fs column is the exact dataset name and therefore unambiguous, unlike the mountpoint
|
|
141
|
+
const dataset = byName[item.fs] || byMount[item.mount];
|
|
142
|
+
if (!dataset || !(dataset.used + dataset.available)) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
item.used = dataset.used;
|
|
146
|
+
item.available = dataset.available;
|
|
147
|
+
item.size = dataset.used + dataset.available;
|
|
148
|
+
item.use = parseFloat(((100.0 * dataset.used) / item.size).toFixed(2));
|
|
149
|
+
});
|
|
150
|
+
cb(data);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
89
154
|
function parseDf(lines) {
|
|
90
155
|
const data = [];
|
|
91
156
|
// filesystem (first column) and mount point (last column) may contain spaces:
|
|
@@ -98,7 +163,7 @@ function fsSize(drive, callback) {
|
|
|
98
163
|
const parts = line.trim().match(hasType ? dfWithType : dfNoType);
|
|
99
164
|
if (parts && (parts[1].startsWith('/') || parts[hasType ? 6 : 5] === '/' || parts[1].indexOf('/') > 0 || parts[1].indexOf(':') === 1 || (!_darwin && !isLinuxTmpFs(parts[2])))) {
|
|
100
165
|
const fs = parts[1];
|
|
101
|
-
const fsType = hasType ? parts[2] :
|
|
166
|
+
const fsType = hasType ? parts[2] : macOsFsType(parts[1]);
|
|
102
167
|
const size = parseInt(parts[hasType ? 3 : 2], 10) * 1024;
|
|
103
168
|
const used = parseInt(parts[hasType ? 4 : 3], 10) * 1024;
|
|
104
169
|
const available = parseInt(parts[hasType ? 5 : 4], 10) * 1024;
|
|
@@ -142,11 +207,16 @@ function fsSize(drive, callback) {
|
|
|
142
207
|
execSync('mount')
|
|
143
208
|
.toString()
|
|
144
209
|
.split('\n')
|
|
145
|
-
.filter((line) => {
|
|
146
|
-
return line.startsWith('/');
|
|
147
|
-
})
|
|
148
210
|
.forEach((line) => {
|
|
149
|
-
|
|
211
|
+
// mount output: "<fs> on <mountpoint> (<type>, <options>)"
|
|
212
|
+
const fs = line.split(' ')[0];
|
|
213
|
+
const type = line.match(/\(([^),]+)[^)]*\)$/);
|
|
214
|
+
if (fs && type) {
|
|
215
|
+
macOsFsTypes.set(fs, type[1].trim().toLowerCase());
|
|
216
|
+
}
|
|
217
|
+
if (line.startsWith('/')) {
|
|
218
|
+
osMounts[fs] = line.toLowerCase().indexOf('read-only') === -1;
|
|
219
|
+
}
|
|
150
220
|
});
|
|
151
221
|
} catch {
|
|
152
222
|
util.noop();
|
|
@@ -195,19 +265,23 @@ function fsSize(drive, callback) {
|
|
|
195
265
|
});
|
|
196
266
|
}
|
|
197
267
|
if ((!error || data.length) && stdout.toString().trim() !== '') {
|
|
198
|
-
|
|
199
|
-
callback
|
|
200
|
-
|
|
201
|
-
|
|
268
|
+
applyZfsUsage(data, (data) => {
|
|
269
|
+
if (callback) {
|
|
270
|
+
callback(data);
|
|
271
|
+
}
|
|
272
|
+
resolve(data);
|
|
273
|
+
});
|
|
202
274
|
} else {
|
|
203
275
|
exec('df -kPT 2>/dev/null', { maxBuffer: 1024 * 1024 }, (error, stdout) => {
|
|
204
276
|
// fixed issue alpine fallback
|
|
205
277
|
const lines = filterLines(stdout);
|
|
206
278
|
data = parseDf(lines);
|
|
207
|
-
|
|
208
|
-
callback
|
|
209
|
-
|
|
210
|
-
|
|
279
|
+
applyZfsUsage(data, (data) => {
|
|
280
|
+
if (callback) {
|
|
281
|
+
callback(data);
|
|
282
|
+
}
|
|
283
|
+
resolve(data);
|
|
284
|
+
});
|
|
211
285
|
});
|
|
212
286
|
}
|
|
213
287
|
});
|
|
@@ -220,8 +294,9 @@ function fsSize(drive, callback) {
|
|
|
220
294
|
}
|
|
221
295
|
if (_windows) {
|
|
222
296
|
try {
|
|
223
|
-
|
|
224
|
-
const
|
|
297
|
+
// invalid drive input yields '' -> filter matches no drive instead of listing all
|
|
298
|
+
const driveSanitized = util.sanitizeDriveLetter(drive);
|
|
299
|
+
const cmd = `Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size ${drive ? "| where -property Caption -eq '" + driveSanitized + "'" : ''} | fl`;
|
|
225
300
|
util.powerShell(cmd).then((stdout, error) => {
|
|
226
301
|
if (!error) {
|
|
227
302
|
const devices = stdout.toString().split(/\n\s*\n/);
|
|
@@ -492,7 +567,9 @@ function raidMatchLinux(data) {
|
|
|
492
567
|
try {
|
|
493
568
|
data.forEach((element) => {
|
|
494
569
|
if (element.type.startsWith('raid')) {
|
|
495
|
-
const lines = execSync(`mdadm --export --detail /dev/${util.sanitizeString(element.name, true)}`, util.execOptsLinux)
|
|
570
|
+
const lines = execSync(`mdadm --export --detail /dev/${util.sanitizeString(element.name, true)}`, util.execOptsLinux)
|
|
571
|
+
.toString()
|
|
572
|
+
.split('\n');
|
|
496
573
|
const mdData = decodeMdabmData(lines);
|
|
497
574
|
|
|
498
575
|
element.label = mdData.label; // <- assign label info
|
|
@@ -1359,7 +1436,7 @@ function diskLayout(callback) {
|
|
|
1359
1436
|
}
|
|
1360
1437
|
if (_darwin) {
|
|
1361
1438
|
let cmdFullSmart = '';
|
|
1362
|
-
exec(`system_profiler SPSerialATADataType SPNVMeDataType SPUSBDataType SPStorageDataType`, { maxBuffer: 1024 * 1024 }, (error, stdout) => {
|
|
1439
|
+
exec(`system_profiler SPSerialATADataType SPNVMeDataType SPUSBDataType SPUSBHostDataType SPStorageDataType`, { maxBuffer: 1024 * 1024 }, (error, stdout) => {
|
|
1363
1440
|
if (!error) {
|
|
1364
1441
|
// split by type:
|
|
1365
1442
|
const lines = stdout.toString().split('\n');
|
|
@@ -1498,9 +1575,12 @@ function diskLayout(callback) {
|
|
|
1498
1575
|
} catch {
|
|
1499
1576
|
util.noop();
|
|
1500
1577
|
}
|
|
1501
|
-
// USB Drives (
|
|
1578
|
+
// USB Drives (SPUSBDataType up to macOS 26, SPUSBHostDataType from macOS 27 on)
|
|
1502
1579
|
try {
|
|
1503
|
-
const devices = linesUSB
|
|
1580
|
+
const devices = linesUSB
|
|
1581
|
+
.join('\n')
|
|
1582
|
+
.replace(/Media:\n /g, 'Model:')
|
|
1583
|
+
.split('\n\n Product ID:');
|
|
1504
1584
|
devices.shift();
|
|
1505
1585
|
devices.forEach((device) => {
|
|
1506
1586
|
const lines = device.split('\n');
|
|
@@ -1751,8 +1831,8 @@ function diskLayout(callback) {
|
|
|
1751
1831
|
// in first case it will be "<serial number>&0"
|
|
1752
1832
|
// in second case it will be opaque generated value that looks like this: "5&<8-symbol hex code>&0&000000"
|
|
1753
1833
|
// https://learn.microsoft.com/en-us/windows-hardware/drivers/install/instance-ids
|
|
1754
|
-
if (parts.length
|
|
1755
|
-
serialNum = parts[0]
|
|
1834
|
+
if (parts.length === 2 && parts[1] === '0') {
|
|
1835
|
+
serialNum = parts[0];
|
|
1756
1836
|
}
|
|
1757
1837
|
}
|
|
1758
1838
|
}
|
package/lib/network.js
CHANGED
package/lib/osinfo.js
CHANGED
|
@@ -1164,16 +1164,30 @@ function shell(callback) {
|
|
|
1164
1164
|
|
|
1165
1165
|
exports.shell = shell;
|
|
1166
1166
|
|
|
1167
|
+
// macOS 26+ and some hardened linux kernels mask MACs in getifaddrs()
|
|
1168
|
+
const MASKED_MACS = ['00:00:00:00:00:00', '02:00:00:00:00:00'];
|
|
1169
|
+
|
|
1167
1170
|
function getUniqueMacAdresses() {
|
|
1168
1171
|
let macs = [];
|
|
1169
1172
|
try {
|
|
1170
1173
|
const ifaces = os.networkInterfaces();
|
|
1174
|
+
let fallbackMacs = null;
|
|
1171
1175
|
for (let dev in ifaces) {
|
|
1172
1176
|
if ({}.hasOwnProperty.call(ifaces, dev)) {
|
|
1173
1177
|
ifaces[dev].forEach((details) => {
|
|
1174
|
-
if (details && details.mac
|
|
1175
|
-
|
|
1176
|
-
if (
|
|
1178
|
+
if (details && details.mac) {
|
|
1179
|
+
let mac = details.mac.toLowerCase();
|
|
1180
|
+
if (MASKED_MACS.indexOf(mac) >= 0) {
|
|
1181
|
+
if (fallbackMacs === null) {
|
|
1182
|
+
try {
|
|
1183
|
+
fallbackMacs = require('./network').getMacAddresses();
|
|
1184
|
+
} catch {
|
|
1185
|
+
fallbackMacs = {};
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
mac = (fallbackMacs[dev] || '').toLowerCase();
|
|
1189
|
+
}
|
|
1190
|
+
if (mac && MASKED_MACS.indexOf(mac) === -1 && macs.indexOf(mac) === -1) {
|
|
1177
1191
|
macs.push(mac);
|
|
1178
1192
|
}
|
|
1179
1193
|
}
|
package/lib/processes.js
CHANGED
|
@@ -261,12 +261,20 @@ function services(srv, callback) {
|
|
|
261
261
|
});
|
|
262
262
|
if (_linux) {
|
|
263
263
|
// calc process_cpu - ps is not accurate in linux!
|
|
264
|
+
// ps pcpu is a lifetime average, the /proc values below are an interval share -
|
|
265
|
+
// drop the ps seed instead of adding both (#1007)
|
|
266
|
+
result.forEach((item) => {
|
|
267
|
+
item.cpu = 0;
|
|
268
|
+
});
|
|
264
269
|
let cmd = 'cat /proc/stat | grep "cpu "';
|
|
265
270
|
for (let i in result) {
|
|
266
271
|
for (let j in result[i].pids) {
|
|
267
272
|
cmd += ';cat /proc/' + result[i].pids[j] + '/stat';
|
|
268
273
|
}
|
|
269
274
|
}
|
|
275
|
+
// freeze the baseline before the async call - a concurrent call overwrites _services_cpu
|
|
276
|
+
// and would leave this one dividing by a few jiffies (#1007)
|
|
277
|
+
const cpuBaseline = Object.assign({}, _services_cpu);
|
|
270
278
|
exec(cmd, { maxBuffer: 1024 * 102400 }, function (error, stdout) {
|
|
271
279
|
let curr_processes = stdout.toString().split('\n');
|
|
272
280
|
|
|
@@ -277,7 +285,7 @@ function services(srv, callback) {
|
|
|
277
285
|
let list_new = {};
|
|
278
286
|
let resultProcess = {};
|
|
279
287
|
curr_processes.forEach((element) => {
|
|
280
|
-
resultProcess = calcProcStatLinux(element, all,
|
|
288
|
+
resultProcess = calcProcStatLinux(element, all, cpuBaseline);
|
|
281
289
|
|
|
282
290
|
if (resultProcess.pid) {
|
|
283
291
|
let listPos = -1;
|
|
@@ -297,9 +305,7 @@ function services(srv, callback) {
|
|
|
297
305
|
cpuu: resultProcess.cpuu,
|
|
298
306
|
cpus: resultProcess.cpus,
|
|
299
307
|
utime: resultProcess.utime,
|
|
300
|
-
stime: resultProcess.stime
|
|
301
|
-
cutime: resultProcess.cutime,
|
|
302
|
-
cstime: resultProcess.cstime
|
|
308
|
+
stime: resultProcess.stime
|
|
303
309
|
};
|
|
304
310
|
}
|
|
305
311
|
});
|
|
@@ -474,6 +480,19 @@ function parseProcStat(line) {
|
|
|
474
480
|
return user + nice + system + idle + iowait + irq + softirq + steal + guest + guest_nice;
|
|
475
481
|
}
|
|
476
482
|
|
|
483
|
+
// drops NaN/Infinity/negative values and scales cpuu + cpus down proportionally
|
|
484
|
+
// if their sum exceeds 100 (normalized against all cores)
|
|
485
|
+
function clampCpuPair(cpuu, cpus) {
|
|
486
|
+
if (!isFinite(cpuu) || cpuu < 0) { cpuu = 0; }
|
|
487
|
+
if (!isFinite(cpus) || cpus < 0) { cpus = 0; }
|
|
488
|
+
const total = cpuu + cpus;
|
|
489
|
+
if (total > 100) {
|
|
490
|
+
cpuu = (cpuu / total) * 100;
|
|
491
|
+
cpus = (cpus / total) * 100;
|
|
492
|
+
}
|
|
493
|
+
return { cpuu: cpuu, cpus: cpus };
|
|
494
|
+
}
|
|
495
|
+
|
|
477
496
|
function calcProcStatLinux(line, all, _cpu_old) {
|
|
478
497
|
let statparts = line.replace(/ +/g, ' ').split(')');
|
|
479
498
|
if (statparts.length >= 2) {
|
|
@@ -482,35 +501,34 @@ function calcProcStatLinux(line, all, _cpu_old) {
|
|
|
482
501
|
let pid = parseInt(statparts[0].split(' ')[0]);
|
|
483
502
|
let utime = parseInt(parts[12]);
|
|
484
503
|
let stime = parseInt(parts[13]);
|
|
485
|
-
let cutime = parseInt(parts[14]);
|
|
486
|
-
let cstime = parseInt(parts[15]);
|
|
487
504
|
|
|
488
|
-
// calc
|
|
505
|
+
// calc - child times (cutime/cstime) are deliberately left out: reaping a child adds its
|
|
506
|
+
// whole lifetime in one interval, which is what produced the >100% spikes in #1007.
|
|
507
|
+
// top, htop and Task Manager exclude them too.
|
|
489
508
|
let cpuu = 0;
|
|
490
509
|
let cpus = 0;
|
|
491
510
|
if (_cpu_old.all > 0 && _cpu_old.list[pid]) {
|
|
492
|
-
|
|
493
|
-
|
|
511
|
+
const delta = all - _cpu_old.all;
|
|
512
|
+
cpuu = delta > 0 ? ((utime - _cpu_old.list[pid].utime) / delta) * 100 : 0; // user
|
|
513
|
+
cpus = delta > 0 ? ((stime - _cpu_old.list[pid].stime) / delta) * 100 : 0; // system
|
|
494
514
|
} else {
|
|
495
|
-
cpuu = (
|
|
496
|
-
cpus = (
|
|
515
|
+
cpuu = all > 0 ? (utime / all) * 100 : 0; // user
|
|
516
|
+
cpus = all > 0 ? (stime / all) * 100 : 0; // system
|
|
497
517
|
}
|
|
518
|
+
// normalized against all cores, so 100 is the ceiling for cpuu + cpus
|
|
519
|
+
const clamped = clampCpuPair(cpuu, cpus);
|
|
498
520
|
return {
|
|
499
521
|
pid: pid,
|
|
500
522
|
utime: utime,
|
|
501
523
|
stime: stime,
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
cpuu: cpuu,
|
|
505
|
-
cpus: cpus
|
|
524
|
+
cpuu: clamped.cpuu,
|
|
525
|
+
cpus: clamped.cpus
|
|
506
526
|
};
|
|
507
527
|
} else {
|
|
508
528
|
return {
|
|
509
529
|
pid: 0,
|
|
510
530
|
utime: 0,
|
|
511
531
|
stime: 0,
|
|
512
|
-
cutime: 0,
|
|
513
|
-
cstime: 0,
|
|
514
532
|
cpuu: 0,
|
|
515
533
|
cpus: 0
|
|
516
534
|
};
|
|
@@ -520,8 +538,6 @@ function calcProcStatLinux(line, all, _cpu_old) {
|
|
|
520
538
|
pid: 0,
|
|
521
539
|
utime: 0,
|
|
522
540
|
stime: 0,
|
|
523
|
-
cutime: 0,
|
|
524
|
-
cstime: 0,
|
|
525
541
|
cpuu: 0,
|
|
526
542
|
cpus: 0
|
|
527
543
|
};
|
|
@@ -533,18 +549,21 @@ function calcProcStatWin(procStat, all, _cpu_old) {
|
|
|
533
549
|
let cpuu = 0;
|
|
534
550
|
let cpus = 0;
|
|
535
551
|
if (_cpu_old.all > 0 && _cpu_old.list[procStat.pid]) {
|
|
536
|
-
|
|
537
|
-
|
|
552
|
+
const delta = all - _cpu_old.all;
|
|
553
|
+
cpuu = delta > 0 ? ((procStat.utime - _cpu_old.list[procStat.pid].utime) / delta) * 100 : 0; // user
|
|
554
|
+
cpus = delta > 0 ? ((procStat.stime - _cpu_old.list[procStat.pid].stime) / delta) * 100 : 0; // system
|
|
538
555
|
} else {
|
|
539
|
-
cpuu = (procStat.utime / all) * 100; // user
|
|
540
|
-
cpus = (procStat.stime / all) * 100; // system
|
|
556
|
+
cpuu = all > 0 ? (procStat.utime / all) * 100 : 0; // user
|
|
557
|
+
cpus = all > 0 ? (procStat.stime / all) * 100 : 0; // system
|
|
541
558
|
}
|
|
559
|
+
// same ceiling as the linux path - cpuu + cpus stays inside [0, 100] (#1007)
|
|
560
|
+
const clamped = clampCpuPair(cpuu, cpus);
|
|
542
561
|
return {
|
|
543
562
|
pid: procStat.pid,
|
|
544
563
|
utime: procStat.utime,
|
|
545
564
|
stime: procStat.stime,
|
|
546
|
-
cpuu: cpuu
|
|
547
|
-
cpus: cpus
|
|
565
|
+
cpuu: clamped.cpuu,
|
|
566
|
+
cpus: clamped.cpus
|
|
548
567
|
};
|
|
549
568
|
}
|
|
550
569
|
|
|
@@ -637,6 +656,11 @@ function processes(callback) {
|
|
|
637
656
|
let command = '';
|
|
638
657
|
let params = '';
|
|
639
658
|
let fullcommand = line.substring(parsedhead[12].from + offset, parsedhead[12].to + offset2).trim();
|
|
659
|
+
// zombies are printed as "[name] <defunct>" - drop the marker so the bracket handling below
|
|
660
|
+
// sees a plain "[name]" and does not leak "] <defunct>" into command and name
|
|
661
|
+
if (fullcommand.endsWith(' <defunct>')) {
|
|
662
|
+
fullcommand = fullcommand.slice(0, -10).trim();
|
|
663
|
+
}
|
|
640
664
|
if (fullcommand.substr(fullcommand.length - 1) === ']') {
|
|
641
665
|
fullcommand = fullcommand.slice(0, -1);
|
|
642
666
|
}
|
|
@@ -855,6 +879,9 @@ function processes(callback) {
|
|
|
855
879
|
result.list.forEach((element) => {
|
|
856
880
|
cmd += ';cat /proc/' + element.pid + '/stat';
|
|
857
881
|
});
|
|
882
|
+
// freeze the baseline before the async call - a concurrent call overwrites _processes_cpu
|
|
883
|
+
// and would leave this one dividing by a few jiffies (#1007)
|
|
884
|
+
const cpuBaseline = Object.assign({}, _processes_cpu);
|
|
858
885
|
exec(cmd, { maxBuffer: 1024 * 102400 }, (error, stdout) => {
|
|
859
886
|
let curr_processes = stdout.toString().split('\n');
|
|
860
887
|
|
|
@@ -865,7 +892,7 @@ function processes(callback) {
|
|
|
865
892
|
let list_new = {};
|
|
866
893
|
let resultProcess = {};
|
|
867
894
|
curr_processes.forEach((element) => {
|
|
868
|
-
resultProcess = calcProcStatLinux(element, all,
|
|
895
|
+
resultProcess = calcProcStatLinux(element, all, cpuBaseline);
|
|
869
896
|
|
|
870
897
|
if (resultProcess.pid) {
|
|
871
898
|
// store pcpu in outer array
|
|
@@ -885,9 +912,7 @@ function processes(callback) {
|
|
|
885
912
|
cpuu: resultProcess.cpuu,
|
|
886
913
|
cpus: resultProcess.cpus,
|
|
887
914
|
utime: resultProcess.utime,
|
|
888
|
-
stime: resultProcess.stime
|
|
889
|
-
cutime: resultProcess.cutime,
|
|
890
|
-
cstime: resultProcess.cstime
|
|
915
|
+
stime: resultProcess.stime
|
|
891
916
|
};
|
|
892
917
|
}
|
|
893
918
|
});
|
|
@@ -950,6 +975,9 @@ function processes(callback) {
|
|
|
950
975
|
}
|
|
951
976
|
} else if (_windows) {
|
|
952
977
|
try {
|
|
978
|
+
// freeze the baseline before the async call - a concurrent call overwrites _processes_cpu
|
|
979
|
+
// and would leave this one with a non positive delta (#1007)
|
|
980
|
+
const cpuBaseline = Object.assign({}, _processes_cpu);
|
|
953
981
|
util
|
|
954
982
|
.powerShell(
|
|
955
983
|
`Get-CimInstance Win32_Process | select-Object ProcessId,ParentProcessId,ExecutionState,Caption,CommandLine,ExecutablePath,UserModeTime,KernelModeTime,WorkingSetSize,Priority,PageFileUsage,
|
|
@@ -960,8 +988,10 @@ function processes(callback) {
|
|
|
960
988
|
const procs = [];
|
|
961
989
|
const procStats = [];
|
|
962
990
|
const list_new = {};
|
|
963
|
-
|
|
964
|
-
|
|
991
|
+
// accumulate from the previous totals and add deltas only - a process that exited
|
|
992
|
+
// must not lower the total, otherwise the denominator turns negative (#559)
|
|
993
|
+
let allcpuu = cpuBaseline.all_utime;
|
|
994
|
+
let allcpus = cpuBaseline.all_stime;
|
|
965
995
|
let processArray = [];
|
|
966
996
|
try {
|
|
967
997
|
stdout = stdout.trim().replace(/^\uFEFF/, '');
|
|
@@ -981,8 +1011,9 @@ function processes(callback) {
|
|
|
981
1011
|
const utime = element.UserModeTime;
|
|
982
1012
|
const stime = element.KernelModeTime;
|
|
983
1013
|
const memw = element.WorkingSetSize;
|
|
984
|
-
|
|
985
|
-
|
|
1014
|
+
const cpuOld = cpuBaseline.list[pid];
|
|
1015
|
+
allcpuu += utime - (cpuOld ? cpuOld.utime : 0);
|
|
1016
|
+
allcpus += stime - (cpuOld ? cpuOld.stime : 0);
|
|
986
1017
|
result.all++;
|
|
987
1018
|
if (!statusValue) {
|
|
988
1019
|
result.unknown++;
|
|
@@ -1027,7 +1058,7 @@ function processes(callback) {
|
|
|
1027
1058
|
result.sleeping = result.all - result.running - result.blocked - result.unknown;
|
|
1028
1059
|
result.list = procs;
|
|
1029
1060
|
procStats.forEach((element) => {
|
|
1030
|
-
let resultProcess = calcProcStatWin(element, allcpuu + allcpus,
|
|
1061
|
+
let resultProcess = calcProcStatWin(element, allcpuu + allcpus, cpuBaseline);
|
|
1031
1062
|
|
|
1032
1063
|
// store pcpu in outer array
|
|
1033
1064
|
let listPos = result.list.map((e) => e.pid).indexOf(resultProcess.pid);
|
|
@@ -1150,12 +1181,16 @@ function processLoad(proc, callback) {
|
|
|
1150
1181
|
if (procSanitized && processes.length && processes[0] !== '------') {
|
|
1151
1182
|
if (_windows) {
|
|
1152
1183
|
try {
|
|
1184
|
+
// freeze the baseline before the async call - a concurrent call overwrites _process_cpu
|
|
1185
|
+
// and would leave this one with a non positive delta (#1007)
|
|
1186
|
+
const cpuBaseline = Object.assign({}, _process_cpu);
|
|
1153
1187
|
util.powerShell('Get-CimInstance Win32_Process | select ProcessId,Caption,UserModeTime,KernelModeTime,WorkingSetSize | ConvertTo-Json -compress').then((stdout, error) => {
|
|
1154
1188
|
if (!error) {
|
|
1155
1189
|
const procStats = [];
|
|
1156
1190
|
const list_new = {};
|
|
1157
|
-
|
|
1158
|
-
let
|
|
1191
|
+
// see processes() - never lower the total when a process exits (#559)
|
|
1192
|
+
let allcpuu = cpuBaseline.all_utime;
|
|
1193
|
+
let allcpus = cpuBaseline.all_stime;
|
|
1159
1194
|
let processArray = [];
|
|
1160
1195
|
try {
|
|
1161
1196
|
stdout = stdout.trim().replace(/^\uFEFF/, '');
|
|
@@ -1172,8 +1207,9 @@ function processLoad(proc, callback) {
|
|
|
1172
1207
|
const utime = element.UserModeTime;
|
|
1173
1208
|
const stime = element.KernelModeTime;
|
|
1174
1209
|
const mem = element.WorkingSetSize;
|
|
1175
|
-
|
|
1176
|
-
|
|
1210
|
+
const cpuOld = cpuBaseline.list[pid];
|
|
1211
|
+
allcpuu += utime - (cpuOld ? cpuOld.utime : 0);
|
|
1212
|
+
allcpus += stime - (cpuOld ? cpuOld.stime : 0);
|
|
1177
1213
|
|
|
1178
1214
|
procStats.push({
|
|
1179
1215
|
pid: pid,
|
|
@@ -1231,7 +1267,7 @@ function processLoad(proc, callback) {
|
|
|
1231
1267
|
|
|
1232
1268
|
// calculate proc stats for each proc
|
|
1233
1269
|
procStats.forEach((element) => {
|
|
1234
|
-
let resultProcess = calcProcStatWin(element, allcpuu + allcpus,
|
|
1270
|
+
let resultProcess = calcProcStatWin(element, allcpuu + allcpus, cpuBaseline);
|
|
1235
1271
|
|
|
1236
1272
|
let listPos = -1;
|
|
1237
1273
|
for (let j = 0; j < result.length; j++) {
|
|
@@ -1377,6 +1413,9 @@ function processLoad(proc, callback) {
|
|
|
1377
1413
|
cmd += ';cat /proc/' + result[i].pids[j] + '/stat';
|
|
1378
1414
|
}
|
|
1379
1415
|
}
|
|
1416
|
+
// freeze the baseline before the async call - a concurrent call overwrites _process_cpu
|
|
1417
|
+
// and would leave this one dividing by a few jiffies (#1007)
|
|
1418
|
+
const cpuBaseline = Object.assign({}, _process_cpu);
|
|
1380
1419
|
exec(cmd, { maxBuffer: 1024 * 102400 }, (error, stdout) => {
|
|
1381
1420
|
let curr_processes = stdout.toString().split('\n');
|
|
1382
1421
|
|
|
@@ -1387,7 +1426,7 @@ function processLoad(proc, callback) {
|
|
|
1387
1426
|
let list_new = {};
|
|
1388
1427
|
let resultProcess = {};
|
|
1389
1428
|
curr_processes.forEach((element) => {
|
|
1390
|
-
resultProcess = calcProcStatLinux(element, all,
|
|
1429
|
+
resultProcess = calcProcStatLinux(element, all, cpuBaseline);
|
|
1391
1430
|
|
|
1392
1431
|
if (resultProcess.pid) {
|
|
1393
1432
|
// find result item
|
|
@@ -1407,9 +1446,7 @@ function processLoad(proc, callback) {
|
|
|
1407
1446
|
cpuu: resultProcess.cpuu,
|
|
1408
1447
|
cpus: resultProcess.cpus,
|
|
1409
1448
|
utime: resultProcess.utime,
|
|
1410
|
-
stime: resultProcess.stime
|
|
1411
|
-
cutime: resultProcess.cutime,
|
|
1412
|
-
cstime: resultProcess.cstime
|
|
1449
|
+
stime: resultProcess.stime
|
|
1413
1450
|
};
|
|
1414
1451
|
}
|
|
1415
1452
|
});
|
package/lib/util.js
CHANGED
|
@@ -772,6 +772,12 @@ function sanitizeContainerID(str) {
|
|
|
772
772
|
return s.indexOf('..') === -1 ? s : '';
|
|
773
773
|
}
|
|
774
774
|
|
|
775
|
+
// windows drive letter whitelist: only "<letter>:" survives, everything else is dropped
|
|
776
|
+
function sanitizeDriveLetter(str) {
|
|
777
|
+
const match = /^\s*([a-zA-Z]):?[\\/]?\s*$/.exec(String(str || ''));
|
|
778
|
+
return match ? match[1] + ':' : '';
|
|
779
|
+
}
|
|
780
|
+
|
|
775
781
|
function sanitizeImageID(str) {
|
|
776
782
|
const s = String(str || '')
|
|
777
783
|
.substring(0, 2000)
|
|
@@ -2805,6 +2811,7 @@ exports.sanitizeContainerID = sanitizeContainerID;
|
|
|
2805
2811
|
exports.sanitizeImageID = sanitizeImageID;
|
|
2806
2812
|
exports.isPrototypePolluted = isPrototypePolluted;
|
|
2807
2813
|
exports.sanitizeString = sanitizeString;
|
|
2814
|
+
exports.sanitizeDriveLetter = sanitizeDriveLetter;
|
|
2808
2815
|
exports.decodePiCpuinfo = decodePiCpuinfo;
|
|
2809
2816
|
exports.getRpiGpu = getRpiGpu;
|
|
2810
2817
|
exports.promiseAll = promiseAll;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@depup/systeminformation",
|
|
3
|
-
"version": "5.33.
|
|
3
|
+
"version": "5.33.12-depup.0",
|
|
4
4
|
"description": "Advanced, lightweight system and OS information library (with updated dependencies)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Sebastian Hildebrandt <hildebrandt@plus-innovations.com> (https://plus-innovations.com)",
|
|
@@ -111,8 +111,8 @@
|
|
|
111
111
|
"changes": {},
|
|
112
112
|
"depsUpdated": 0,
|
|
113
113
|
"originalPackage": "systeminformation",
|
|
114
|
-
"originalVersion": "5.33.
|
|
115
|
-
"processedAt": "2026-09-
|
|
114
|
+
"originalVersion": "5.33.12",
|
|
115
|
+
"processedAt": "2026-09-20T01:02:37.142Z",
|
|
116
116
|
"smokeTest": "passed"
|
|
117
117
|
}
|
|
118
118
|
}
|