systeminformation 5.27.13 → 5.27.14
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/lib/filesystem.js +495 -340
- package/lib/util.js +250 -171
- package/package.json +1 -1
package/lib/filesystem.js
CHANGED
|
@@ -20,24 +20,23 @@ const exec = require('child_process').exec;
|
|
|
20
20
|
const execSync = require('child_process').execSync;
|
|
21
21
|
const execPromiseSave = util.promisifySave(require('child_process').exec);
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
const _platform = process.platform;
|
|
24
24
|
|
|
25
|
-
const _linux =
|
|
26
|
-
const _darwin =
|
|
27
|
-
const _windows =
|
|
28
|
-
const _freebsd =
|
|
29
|
-
const _openbsd =
|
|
30
|
-
const _netbsd =
|
|
31
|
-
const _sunos =
|
|
25
|
+
const _linux = _platform === 'linux' || _platform === 'android';
|
|
26
|
+
const _darwin = _platform === 'darwin';
|
|
27
|
+
const _windows = _platform === 'win32';
|
|
28
|
+
const _freebsd = _platform === 'freebsd';
|
|
29
|
+
const _openbsd = _platform === 'openbsd';
|
|
30
|
+
const _netbsd = _platform === 'netbsd';
|
|
31
|
+
const _sunos = _platform === 'sunos';
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
const _fs_speed = {};
|
|
34
|
+
const _disk_io = {};
|
|
35
35
|
|
|
36
36
|
// --------------------------
|
|
37
37
|
// FS - mounted file systems
|
|
38
38
|
|
|
39
39
|
function fsSize(drive, callback) {
|
|
40
|
-
|
|
41
40
|
if (util.isFunction(drive)) {
|
|
42
41
|
callback = drive;
|
|
43
42
|
drive = '';
|
|
@@ -47,30 +46,36 @@ function fsSize(drive, callback) {
|
|
|
47
46
|
let osMounts = [];
|
|
48
47
|
|
|
49
48
|
function getmacOsFsType(fs) {
|
|
50
|
-
if (!fs.startsWith('/')) {
|
|
49
|
+
if (!fs.startsWith('/')) {
|
|
50
|
+
return 'NFS';
|
|
51
|
+
}
|
|
51
52
|
const parts = fs.split('/');
|
|
52
53
|
const fsShort = parts[parts.length - 1];
|
|
53
|
-
const macOsDisksSingle = macOsDisks.filter(item => item.indexOf(fsShort) >= 0);
|
|
54
|
-
if (macOsDisksSingle.length === 1 && macOsDisksSingle[0].indexOf('APFS') >= 0) {
|
|
54
|
+
const macOsDisksSingle = macOsDisks.filter((item) => item.indexOf(fsShort) >= 0);
|
|
55
|
+
if (macOsDisksSingle.length === 1 && macOsDisksSingle[0].indexOf('APFS') >= 0) {
|
|
56
|
+
return 'APFS';
|
|
57
|
+
}
|
|
55
58
|
return 'HFS';
|
|
56
59
|
}
|
|
57
60
|
|
|
58
61
|
function isLinuxTmpFs(fs) {
|
|
59
62
|
const linuxTmpFileSystems = ['rootfs', 'unionfs', 'squashfs', 'cramfs', 'initrd', 'initramfs', 'devtmpfs', 'tmpfs', 'udev', 'devfs', 'specfs', 'type', 'appimaged'];
|
|
60
63
|
let result = false;
|
|
61
|
-
linuxTmpFileSystems.forEach(linuxFs => {
|
|
62
|
-
if (fs.toLowerCase().indexOf(linuxFs) >= 0) {
|
|
64
|
+
linuxTmpFileSystems.forEach((linuxFs) => {
|
|
65
|
+
if (fs.toLowerCase().indexOf(linuxFs) >= 0) {
|
|
66
|
+
result = true;
|
|
67
|
+
}
|
|
63
68
|
});
|
|
64
69
|
return result;
|
|
65
70
|
}
|
|
66
71
|
|
|
67
72
|
function filterLines(stdout) {
|
|
68
|
-
|
|
73
|
+
const lines = stdout.toString().split('\n');
|
|
69
74
|
lines.shift();
|
|
70
75
|
if (stdout.toString().toLowerCase().indexOf('filesystem')) {
|
|
71
76
|
let removeLines = 0;
|
|
72
77
|
for (let i = 0; i < lines.length; i++) {
|
|
73
|
-
if (lines[i]
|
|
78
|
+
if (lines[i]?.toLowerCase().startsWith('filesystem')) {
|
|
74
79
|
removeLines = i;
|
|
75
80
|
}
|
|
76
81
|
}
|
|
@@ -82,21 +87,21 @@ function fsSize(drive, callback) {
|
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
function parseDf(lines) {
|
|
85
|
-
|
|
86
|
-
lines.forEach(
|
|
90
|
+
const data = [];
|
|
91
|
+
lines.forEach((line) => {
|
|
87
92
|
if (line !== '') {
|
|
88
93
|
line = line.replace(/ +/g, ' ').split(' ');
|
|
89
|
-
if (line && (
|
|
94
|
+
if (line && (line[0].startsWith('/') || (line[6] && line[6] === '/') || line[0].indexOf('/') > 0 || line[0].indexOf(':') === 1 || (!_darwin && !isLinuxTmpFs(line[1])))) {
|
|
90
95
|
const fs = line[0];
|
|
91
|
-
const fsType =
|
|
92
|
-
const size = parseInt(
|
|
93
|
-
const used = parseInt(
|
|
94
|
-
const available = parseInt(
|
|
96
|
+
const fsType = _linux || _freebsd || _openbsd || _netbsd ? line[1] : getmacOsFsType(line[0]);
|
|
97
|
+
const size = parseInt(_linux || _freebsd || _openbsd || _netbsd ? line[2] : line[1], 10) * 1024;
|
|
98
|
+
const used = parseInt(_linux || _freebsd || _openbsd || _netbsd ? line[3] : line[2], 10) * 1024;
|
|
99
|
+
const available = parseInt(_linux || _freebsd || _openbsd || _netbsd ? line[4] : line[3], 10) * 1024;
|
|
95
100
|
const use = parseFloat((100.0 * (used / (used + available))).toFixed(2));
|
|
96
|
-
|
|
97
|
-
line.splice(0,
|
|
101
|
+
const rw = osMounts && Object.keys(osMounts).length > 0 ? osMounts[fs] || false : null;
|
|
102
|
+
line.splice(0, _linux || _freebsd || _openbsd || _netbsd ? 6 : 5);
|
|
98
103
|
const mount = line.join(' ');
|
|
99
|
-
if (!data.find(el =>
|
|
104
|
+
if (!data.find((el) => el.fs === fs && el.type === fsType)) {
|
|
100
105
|
data.push({
|
|
101
106
|
fs,
|
|
102
107
|
type: fsType,
|
|
@@ -124,48 +129,62 @@ function fsSize(drive, callback) {
|
|
|
124
129
|
if (_darwin) {
|
|
125
130
|
cmd = 'df -kP';
|
|
126
131
|
try {
|
|
127
|
-
macOsDisks = execSync('diskutil list')
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
132
|
+
macOsDisks = execSync('diskutil list')
|
|
133
|
+
.toString()
|
|
134
|
+
.split('\n')
|
|
135
|
+
.filter((line) => {
|
|
136
|
+
return !line.startsWith('/') && line.indexOf(':') > 0;
|
|
137
|
+
});
|
|
138
|
+
execSync('mount')
|
|
139
|
+
.toString()
|
|
140
|
+
.split('\n')
|
|
141
|
+
.filter((line) => {
|
|
142
|
+
return line.startsWith('/');
|
|
143
|
+
})
|
|
144
|
+
.forEach((line) => {
|
|
145
|
+
osMounts[line.split(' ')[0]] = line.toLowerCase().indexOf('read-only') === -1;
|
|
146
|
+
});
|
|
147
|
+
} catch {
|
|
136
148
|
util.noop();
|
|
137
149
|
}
|
|
138
150
|
}
|
|
139
151
|
if (_linux) {
|
|
140
152
|
try {
|
|
141
153
|
cmd = 'export LC_ALL=C; df -lkPTx squashfs; unset LC_ALL';
|
|
142
|
-
execSync('cat /proc/mounts 2>/dev/null', util.execOptsLinux)
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
154
|
+
execSync('cat /proc/mounts 2>/dev/null', util.execOptsLinux)
|
|
155
|
+
.toString()
|
|
156
|
+
.split('\n')
|
|
157
|
+
.filter((line) => {
|
|
158
|
+
return line.startsWith('/');
|
|
159
|
+
})
|
|
160
|
+
.forEach((line) => {
|
|
161
|
+
osMounts[line.split(' ')[0]] = osMounts[line.split(' ')[0]] || false;
|
|
162
|
+
if (line.toLowerCase().indexOf('/snap/') === -1) {
|
|
163
|
+
osMounts[line.split(' ')[0]] = line.toLowerCase().indexOf('rw,') >= 0 || line.toLowerCase().indexOf(' rw ') >= 0;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
} catch {
|
|
151
167
|
util.noop();
|
|
152
168
|
}
|
|
153
169
|
}
|
|
154
170
|
if (_freebsd || _openbsd || _netbsd) {
|
|
155
171
|
try {
|
|
156
172
|
cmd = 'df -lkPT';
|
|
157
|
-
execSync('mount')
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
173
|
+
execSync('mount')
|
|
174
|
+
.toString()
|
|
175
|
+
.split('\n')
|
|
176
|
+
.forEach((line) => {
|
|
177
|
+
osMounts[line.split(' ')[0]] = line.toLowerCase().indexOf('read-only') === -1;
|
|
178
|
+
});
|
|
179
|
+
} catch {
|
|
161
180
|
util.noop();
|
|
162
181
|
}
|
|
163
182
|
}
|
|
164
|
-
exec(cmd, { maxBuffer: 1024 * 1024 },
|
|
165
|
-
|
|
183
|
+
exec(cmd, { maxBuffer: 1024 * 1024 }, (error, stdout) => {
|
|
184
|
+
const lines = filterLines(stdout);
|
|
166
185
|
data = parseDf(lines);
|
|
167
186
|
if (drive) {
|
|
168
|
-
data = data.filter(item => {
|
|
187
|
+
data = data.filter((item) => {
|
|
169
188
|
return item.fs.toLowerCase().indexOf(drive.toLowerCase()) >= 0 || item.mount.toLowerCase().indexOf(drive.toLowerCase()) >= 0;
|
|
170
189
|
});
|
|
171
190
|
}
|
|
@@ -175,9 +194,9 @@ function fsSize(drive, callback) {
|
|
|
175
194
|
}
|
|
176
195
|
resolve(data);
|
|
177
196
|
} else {
|
|
178
|
-
exec('df -kPT', { maxBuffer: 1024 * 1024 },
|
|
197
|
+
exec('df -kPT', { maxBuffer: 1024 * 1024 }, (error, stdout) => {
|
|
179
198
|
if (!error) {
|
|
180
|
-
|
|
199
|
+
const lines = filterLines(stdout);
|
|
181
200
|
data = parseDf(lines);
|
|
182
201
|
}
|
|
183
202
|
if (callback) {
|
|
@@ -189,22 +208,25 @@ function fsSize(drive, callback) {
|
|
|
189
208
|
});
|
|
190
209
|
}
|
|
191
210
|
if (_sunos) {
|
|
192
|
-
if (callback) {
|
|
211
|
+
if (callback) {
|
|
212
|
+
callback(data);
|
|
213
|
+
}
|
|
193
214
|
resolve(data);
|
|
194
215
|
}
|
|
195
216
|
if (_windows) {
|
|
196
217
|
try {
|
|
197
|
-
const
|
|
218
|
+
const driveSanitized = drive ? util.sanitizeShellString(drive, true) : '';
|
|
219
|
+
const cmd = `Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size ${driveSanitized ? '| where -property Caption -eq ' + driveSanitized : ''} | fl`;
|
|
198
220
|
util.powerShell(cmd).then((stdout, error) => {
|
|
199
221
|
if (!error) {
|
|
200
|
-
|
|
201
|
-
devices.forEach(
|
|
202
|
-
|
|
222
|
+
const devices = stdout.toString().split(/\n\s*\n/);
|
|
223
|
+
devices.forEach((device) => {
|
|
224
|
+
const lines = device.split('\r\n');
|
|
203
225
|
const size = util.toInt(util.getValue(lines, 'size', ':'));
|
|
204
226
|
const free = util.toInt(util.getValue(lines, 'freespace', ':'));
|
|
205
227
|
const caption = util.getValue(lines, 'caption', ':');
|
|
206
228
|
const rwValue = util.getValue(lines, 'access', ':');
|
|
207
|
-
const rw = rwValue ?
|
|
229
|
+
const rw = rwValue ? util.toInt(rwValue) !== 1 : null;
|
|
208
230
|
if (size) {
|
|
209
231
|
data.push({
|
|
210
232
|
fs: caption,
|
|
@@ -224,8 +246,10 @@ function fsSize(drive, callback) {
|
|
|
224
246
|
}
|
|
225
247
|
resolve(data);
|
|
226
248
|
});
|
|
227
|
-
} catch
|
|
228
|
-
if (callback) {
|
|
249
|
+
} catch {
|
|
250
|
+
if (callback) {
|
|
251
|
+
callback(data);
|
|
252
|
+
}
|
|
229
253
|
resolve(data);
|
|
230
254
|
}
|
|
231
255
|
}
|
|
@@ -239,7 +263,6 @@ exports.fsSize = fsSize;
|
|
|
239
263
|
// FS - open files count
|
|
240
264
|
|
|
241
265
|
function fsOpenFiles(callback) {
|
|
242
|
-
|
|
243
266
|
return new Promise((resolve) => {
|
|
244
267
|
process.nextTick(() => {
|
|
245
268
|
const result = {
|
|
@@ -248,10 +271,10 @@ function fsOpenFiles(callback) {
|
|
|
248
271
|
available: null
|
|
249
272
|
};
|
|
250
273
|
if (_freebsd || _openbsd || _netbsd || _darwin) {
|
|
251
|
-
|
|
252
|
-
exec(cmd, { maxBuffer: 1024 * 1024 },
|
|
274
|
+
const cmd = 'sysctl -i kern.maxfiles kern.num_files kern.open_files';
|
|
275
|
+
exec(cmd, { maxBuffer: 1024 * 1024 }, (error, stdout) => {
|
|
253
276
|
if (!error) {
|
|
254
|
-
|
|
277
|
+
const lines = stdout.toString().split('\n');
|
|
255
278
|
result.max = parseInt(util.getValue(lines, 'kern.maxfiles', ':'), 10);
|
|
256
279
|
result.allocated = parseInt(util.getValue(lines, 'kern.num_files', ':'), 10) || parseInt(util.getValue(lines, 'kern.open_files', ':'), 10);
|
|
257
280
|
result.available = result.max - result.allocated;
|
|
@@ -263,16 +286,18 @@ function fsOpenFiles(callback) {
|
|
|
263
286
|
});
|
|
264
287
|
}
|
|
265
288
|
if (_linux) {
|
|
266
|
-
fs.readFile('/proc/sys/fs/file-nr',
|
|
289
|
+
fs.readFile('/proc/sys/fs/file-nr', (error, stdout) => {
|
|
267
290
|
if (!error) {
|
|
268
|
-
|
|
291
|
+
const lines = stdout.toString().split('\n');
|
|
269
292
|
if (lines[0]) {
|
|
270
293
|
const parts = lines[0].replace(/\s+/g, ' ').split(' ');
|
|
271
294
|
if (parts.length === 3) {
|
|
272
295
|
result.allocated = parseInt(parts[0], 10);
|
|
273
296
|
result.available = parseInt(parts[1], 10);
|
|
274
297
|
result.max = parseInt(parts[2], 10);
|
|
275
|
-
if (!result.available) {
|
|
298
|
+
if (!result.available) {
|
|
299
|
+
result.available = result.max - result.allocated;
|
|
300
|
+
}
|
|
276
301
|
}
|
|
277
302
|
}
|
|
278
303
|
if (callback) {
|
|
@@ -280,9 +305,9 @@ function fsOpenFiles(callback) {
|
|
|
280
305
|
}
|
|
281
306
|
resolve(result);
|
|
282
307
|
} else {
|
|
283
|
-
fs.readFile('/proc/sys/fs/file-max',
|
|
308
|
+
fs.readFile('/proc/sys/fs/file-max', (error, stdout) => {
|
|
284
309
|
if (!error) {
|
|
285
|
-
|
|
310
|
+
const lines = stdout.toString().split('\n');
|
|
286
311
|
if (lines[0]) {
|
|
287
312
|
result.max = parseInt(lines[0], 10);
|
|
288
313
|
}
|
|
@@ -296,11 +321,15 @@ function fsOpenFiles(callback) {
|
|
|
296
321
|
});
|
|
297
322
|
}
|
|
298
323
|
if (_sunos) {
|
|
299
|
-
if (callback) {
|
|
324
|
+
if (callback) {
|
|
325
|
+
callback(null);
|
|
326
|
+
}
|
|
300
327
|
resolve(null);
|
|
301
328
|
}
|
|
302
329
|
if (_windows) {
|
|
303
|
-
if (callback) {
|
|
330
|
+
if (callback) {
|
|
331
|
+
callback(null);
|
|
332
|
+
}
|
|
304
333
|
resolve(null);
|
|
305
334
|
}
|
|
306
335
|
});
|
|
@@ -313,18 +342,18 @@ exports.fsOpenFiles = fsOpenFiles;
|
|
|
313
342
|
// disks
|
|
314
343
|
|
|
315
344
|
function parseBytes(s) {
|
|
316
|
-
return parseInt(s.substr(s.indexOf(' (') + 2, s.indexOf(' Bytes)') - 10));
|
|
345
|
+
return parseInt(s.substr(s.indexOf(' (') + 2, s.indexOf(' Bytes)') - 10), 10);
|
|
317
346
|
}
|
|
318
347
|
|
|
319
348
|
function parseDevices(lines) {
|
|
320
|
-
|
|
349
|
+
const devices = [];
|
|
321
350
|
let i = 0;
|
|
322
|
-
lines.forEach(line => {
|
|
351
|
+
lines.forEach((line) => {
|
|
323
352
|
if (line.length > 0) {
|
|
324
353
|
if (line[0] === '*') {
|
|
325
354
|
i++;
|
|
326
355
|
} else {
|
|
327
|
-
|
|
356
|
+
const parts = line.split(':');
|
|
328
357
|
if (parts.length > 1) {
|
|
329
358
|
if (!devices[i]) {
|
|
330
359
|
devices[i] = {
|
|
@@ -347,22 +376,50 @@ function parseDevices(lines) {
|
|
|
347
376
|
}
|
|
348
377
|
parts[0] = parts[0].trim().toUpperCase().replace(/ +/g, '');
|
|
349
378
|
parts[1] = parts[1].trim();
|
|
350
|
-
if ('DEVICEIDENTIFIER' === parts[0]) {
|
|
351
|
-
|
|
379
|
+
if ('DEVICEIDENTIFIER' === parts[0]) {
|
|
380
|
+
devices[i].identifier = parts[1];
|
|
381
|
+
}
|
|
382
|
+
if ('DEVICENODE' === parts[0]) {
|
|
383
|
+
devices[i].name = parts[1];
|
|
384
|
+
}
|
|
352
385
|
if ('VOLUMENAME' === parts[0]) {
|
|
353
|
-
if (parts[1].indexOf('Not applicable') === -1) {
|
|
386
|
+
if (parts[1].indexOf('Not applicable') === -1) {
|
|
387
|
+
devices[i].label = parts[1];
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if ('PROTOCOL' === parts[0]) {
|
|
391
|
+
devices[i].protocol = parts[1];
|
|
392
|
+
}
|
|
393
|
+
if ('DISKSIZE' === parts[0]) {
|
|
394
|
+
devices[i].size = parseBytes(parts[1]);
|
|
395
|
+
}
|
|
396
|
+
if ('FILESYSTEMPERSONALITY' === parts[0]) {
|
|
397
|
+
devices[i].fsType = parts[1];
|
|
398
|
+
}
|
|
399
|
+
if ('MOUNTPOINT' === parts[0]) {
|
|
400
|
+
devices[i].mount = parts[1];
|
|
401
|
+
}
|
|
402
|
+
if ('VOLUMEUUID' === parts[0]) {
|
|
403
|
+
devices[i].uuid = parts[1];
|
|
404
|
+
}
|
|
405
|
+
if ('READ-ONLYMEDIA' === parts[0] && parts[1] === 'Yes') {
|
|
406
|
+
devices[i].physical = 'CD/DVD';
|
|
407
|
+
}
|
|
408
|
+
if ('SOLIDSTATE' === parts[0] && parts[1] === 'Yes') {
|
|
409
|
+
devices[i].physical = 'SSD';
|
|
410
|
+
}
|
|
411
|
+
if ('VIRTUAL' === parts[0]) {
|
|
412
|
+
devices[i].type = 'virtual';
|
|
413
|
+
}
|
|
414
|
+
if ('REMOVABLEMEDIA' === parts[0]) {
|
|
415
|
+
devices[i].removable = parts[1] === 'Removable';
|
|
416
|
+
}
|
|
417
|
+
if ('PARTITIONTYPE' === parts[0]) {
|
|
418
|
+
devices[i].type = 'part';
|
|
419
|
+
}
|
|
420
|
+
if ('DEVICE/MEDIANAME' === parts[0]) {
|
|
421
|
+
devices[i].model = parts[1];
|
|
354
422
|
}
|
|
355
|
-
if ('PROTOCOL' === parts[0]) { devices[i].protocol = parts[1]; }
|
|
356
|
-
if ('DISKSIZE' === parts[0]) { devices[i].size = parseBytes(parts[1]); }
|
|
357
|
-
if ('FILESYSTEMPERSONALITY' === parts[0]) { devices[i].fsType = parts[1]; }
|
|
358
|
-
if ('MOUNTPOINT' === parts[0]) { devices[i].mount = parts[1]; }
|
|
359
|
-
if ('VOLUMEUUID' === parts[0]) { devices[i].uuid = parts[1]; }
|
|
360
|
-
if ('READ-ONLYMEDIA' === parts[0] && parts[1] === 'Yes') { devices[i].physical = 'CD/DVD'; }
|
|
361
|
-
if ('SOLIDSTATE' === parts[0] && parts[1] === 'Yes') { devices[i].physical = 'SSD'; }
|
|
362
|
-
if ('VIRTUAL' === parts[0]) { devices[i].type = 'virtual'; }
|
|
363
|
-
if ('REMOVABLEMEDIA' === parts[0]) { devices[i].removable = (parts[1] === 'Removable'); }
|
|
364
|
-
if ('PARTITIONTYPE' === parts[0]) { devices[i].type = 'part'; }
|
|
365
|
-
if ('DEVICE/MEDIANAME' === parts[0]) { devices[i].model = parts[1]; }
|
|
366
423
|
}
|
|
367
424
|
}
|
|
368
425
|
}
|
|
@@ -373,30 +430,32 @@ function parseDevices(lines) {
|
|
|
373
430
|
function parseBlk(lines) {
|
|
374
431
|
let data = [];
|
|
375
432
|
|
|
376
|
-
lines
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
433
|
+
lines
|
|
434
|
+
.filter((line) => line !== '')
|
|
435
|
+
.forEach((line) => {
|
|
436
|
+
try {
|
|
437
|
+
line = decodeURIComponent(line.replace(/\\x/g, '%'));
|
|
438
|
+
line = line.replace(/\\/g, '\\\\');
|
|
439
|
+
const disk = JSON.parse(line);
|
|
440
|
+
data.push({
|
|
441
|
+
name: util.sanitizeShellString(disk.name),
|
|
442
|
+
type: disk.type,
|
|
443
|
+
fsType: disk.fsType,
|
|
444
|
+
mount: disk.mountpoint,
|
|
445
|
+
size: parseInt(disk.size, 10),
|
|
446
|
+
physical: disk.type === 'disk' ? (disk.rota === '0' ? 'SSD' : 'HDD') : disk.type === 'rom' ? 'CD/DVD' : '',
|
|
447
|
+
uuid: disk.uuid,
|
|
448
|
+
label: disk.label,
|
|
449
|
+
model: (disk.model || '').trim(),
|
|
450
|
+
serial: disk.serial,
|
|
451
|
+
removable: disk.rm === '1',
|
|
452
|
+
protocol: disk.tran,
|
|
453
|
+
group: disk.group || ''
|
|
454
|
+
});
|
|
455
|
+
} catch {
|
|
456
|
+
util.noop();
|
|
457
|
+
}
|
|
458
|
+
});
|
|
400
459
|
data = util.unique(data);
|
|
401
460
|
data = util.sortByKey(data, ['type', 'name']);
|
|
402
461
|
return data;
|
|
@@ -407,7 +466,7 @@ function decodeMdabmData(lines) {
|
|
|
407
466
|
const label = util.getValue(lines, 'md_name', '='); // <- get label info
|
|
408
467
|
const uuid = util.getValue(lines, 'md_uuid', '='); // <- get uuid info
|
|
409
468
|
const members = [];
|
|
410
|
-
lines.forEach(line => {
|
|
469
|
+
lines.forEach((line) => {
|
|
411
470
|
if (line.toLowerCase().startsWith('md_device_dev') && line.toLowerCase().indexOf('/dev/') > 0) {
|
|
412
471
|
members.push(line.split('/dev/')[1]);
|
|
413
472
|
}
|
|
@@ -424,7 +483,7 @@ function raidMatchLinux(data) {
|
|
|
424
483
|
// for all block devices of type "raid%"
|
|
425
484
|
let result = data;
|
|
426
485
|
try {
|
|
427
|
-
data.forEach(element => {
|
|
486
|
+
data.forEach((element) => {
|
|
428
487
|
if (element.type.startsWith('raid')) {
|
|
429
488
|
const lines = execSync(`mdadm --export --detail /dev/${element.name}`, util.execOptsLinux).toString().split('\n');
|
|
430
489
|
const mdData = decodeMdabmData(lines);
|
|
@@ -432,8 +491,8 @@ function raidMatchLinux(data) {
|
|
|
432
491
|
element.label = mdData.label; // <- assign label info
|
|
433
492
|
element.uuid = mdData.uuid; // <- assign uuid info
|
|
434
493
|
|
|
435
|
-
if (mdData.members
|
|
436
|
-
result = result.map(blockdevice => {
|
|
494
|
+
if (mdData.members?.length && mdData.raid === element.type) {
|
|
495
|
+
result = result.map((blockdevice) => {
|
|
437
496
|
if (blockdevice.fsType === 'linux_raid_member' && mdData.members.indexOf(blockdevice.name) >= 0) {
|
|
438
497
|
blockdevice.group = element.name;
|
|
439
498
|
}
|
|
@@ -442,7 +501,7 @@ function raidMatchLinux(data) {
|
|
|
442
501
|
}
|
|
443
502
|
}
|
|
444
503
|
});
|
|
445
|
-
} catch
|
|
504
|
+
} catch {
|
|
446
505
|
util.noop();
|
|
447
506
|
}
|
|
448
507
|
return result;
|
|
@@ -450,7 +509,7 @@ function raidMatchLinux(data) {
|
|
|
450
509
|
|
|
451
510
|
function getDevicesLinux(data) {
|
|
452
511
|
const result = [];
|
|
453
|
-
data.forEach(element => {
|
|
512
|
+
data.forEach((element) => {
|
|
454
513
|
if (element.type.startsWith('disk')) {
|
|
455
514
|
result.push(element.name);
|
|
456
515
|
}
|
|
@@ -462,9 +521,9 @@ function matchDevicesLinux(data) {
|
|
|
462
521
|
let result = data;
|
|
463
522
|
try {
|
|
464
523
|
const devices = getDevicesLinux(data);
|
|
465
|
-
result = result.map(blockdevice => {
|
|
524
|
+
result = result.map((blockdevice) => {
|
|
466
525
|
if (blockdevice.type.startsWith('part') || blockdevice.type.startsWith('disk')) {
|
|
467
|
-
devices.forEach(element => {
|
|
526
|
+
devices.forEach((element) => {
|
|
468
527
|
if (blockdevice.name.startsWith(element)) {
|
|
469
528
|
blockdevice.device = '/dev/' + element;
|
|
470
529
|
}
|
|
@@ -472,7 +531,7 @@ function matchDevicesLinux(data) {
|
|
|
472
531
|
}
|
|
473
532
|
return blockdevice;
|
|
474
533
|
});
|
|
475
|
-
} catch
|
|
534
|
+
} catch {
|
|
476
535
|
util.noop();
|
|
477
536
|
}
|
|
478
537
|
return result;
|
|
@@ -480,13 +539,13 @@ function matchDevicesLinux(data) {
|
|
|
480
539
|
|
|
481
540
|
function getDevicesMac(data) {
|
|
482
541
|
const result = [];
|
|
483
|
-
data.forEach(element => {
|
|
542
|
+
data.forEach((element) => {
|
|
484
543
|
if (element.type.startsWith('disk')) {
|
|
485
544
|
result.push({ name: element.name, model: element.model, device: element.name });
|
|
486
545
|
}
|
|
487
546
|
if (element.type.startsWith('virtual')) {
|
|
488
547
|
let device = '';
|
|
489
|
-
result.forEach(e => {
|
|
548
|
+
result.forEach((e) => {
|
|
490
549
|
if (e.model === element.model) {
|
|
491
550
|
device = e.device;
|
|
492
551
|
}
|
|
@@ -503,9 +562,9 @@ function matchDevicesMac(data) {
|
|
|
503
562
|
let result = data;
|
|
504
563
|
try {
|
|
505
564
|
const devices = getDevicesMac(data);
|
|
506
|
-
result = result.map(blockdevice => {
|
|
565
|
+
result = result.map((blockdevice) => {
|
|
507
566
|
if (blockdevice.type.startsWith('part') || blockdevice.type.startsWith('disk') || blockdevice.type.startsWith('virtual')) {
|
|
508
|
-
devices.forEach(element => {
|
|
567
|
+
devices.forEach((element) => {
|
|
509
568
|
if (blockdevice.name.startsWith(element.name)) {
|
|
510
569
|
blockdevice.device = element.device;
|
|
511
570
|
}
|
|
@@ -513,7 +572,7 @@ function matchDevicesMac(data) {
|
|
|
513
572
|
}
|
|
514
573
|
return blockdevice;
|
|
515
574
|
});
|
|
516
|
-
} catch
|
|
575
|
+
} catch {
|
|
517
576
|
util.noop();
|
|
518
577
|
}
|
|
519
578
|
return result;
|
|
@@ -521,13 +580,13 @@ function matchDevicesMac(data) {
|
|
|
521
580
|
|
|
522
581
|
function getDevicesWin(diskDrives) {
|
|
523
582
|
const result = [];
|
|
524
|
-
diskDrives.forEach(element => {
|
|
583
|
+
diskDrives.forEach((element) => {
|
|
525
584
|
const lines = element.split('\r\n');
|
|
526
585
|
const device = util.getValue(lines, 'DeviceID', ':');
|
|
527
586
|
let partitions = element.split('@{DeviceID=');
|
|
528
587
|
if (partitions.length > 1) {
|
|
529
588
|
partitions = partitions.slice(1);
|
|
530
|
-
partitions.forEach(partition => {
|
|
589
|
+
partitions.forEach((partition) => {
|
|
531
590
|
result.push({ name: partition.split(';')[0].toUpperCase(), device });
|
|
532
591
|
});
|
|
533
592
|
}
|
|
@@ -537,8 +596,10 @@ function getDevicesWin(diskDrives) {
|
|
|
537
596
|
|
|
538
597
|
function matchDevicesWin(data, diskDrives) {
|
|
539
598
|
const devices = getDevicesWin(diskDrives);
|
|
540
|
-
data.map(element => {
|
|
541
|
-
const filteresDevices = devices.filter((e) => {
|
|
599
|
+
data.map((element) => {
|
|
600
|
+
const filteresDevices = devices.filter((e) => {
|
|
601
|
+
return e.name === element.name.toUpperCase();
|
|
602
|
+
});
|
|
542
603
|
if (filteresDevices.length > 0) {
|
|
543
604
|
element.device = filteresDevices[0].device;
|
|
544
605
|
}
|
|
@@ -548,7 +609,8 @@ function matchDevicesWin(data, diskDrives) {
|
|
|
548
609
|
}
|
|
549
610
|
|
|
550
611
|
function blkStdoutToObject(stdout) {
|
|
551
|
-
return stdout
|
|
612
|
+
return stdout
|
|
613
|
+
.toString()
|
|
552
614
|
.replace(/NAME=/g, '{"name":')
|
|
553
615
|
.replace(/FSTYPE=/g, ',"fsType":')
|
|
554
616
|
.replace(/TYPE=/g, ',"type":')
|
|
@@ -568,16 +630,15 @@ function blkStdoutToObject(stdout) {
|
|
|
568
630
|
}
|
|
569
631
|
|
|
570
632
|
function blockDevices(callback) {
|
|
571
|
-
|
|
572
633
|
return new Promise((resolve) => {
|
|
573
634
|
process.nextTick(() => {
|
|
574
635
|
let data = [];
|
|
575
636
|
if (_linux) {
|
|
576
637
|
// see https://wiki.ubuntuusers.de/lsblk/
|
|
577
638
|
// exec("lsblk -bo NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,UUID,ROTA,RO,TRAN,SERIAL,LABEL,MODEL,OWNER,GROUP,MODE,ALIGNMENT,MIN-IO,OPT-IO,PHY-SEC,LOG-SEC,SCHED,RQ-SIZE,RA,WSAME", function (error, stdout) {
|
|
578
|
-
const procLsblk1 = exec('lsblk -bPo NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,UUID,ROTA,RO,RM,TRAN,SERIAL,LABEL,MODEL,OWNER 2>/dev/null', { maxBuffer: 1024 * 1024 },
|
|
639
|
+
const procLsblk1 = exec('lsblk -bPo NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,UUID,ROTA,RO,RM,TRAN,SERIAL,LABEL,MODEL,OWNER 2>/dev/null', { maxBuffer: 1024 * 1024 }, (error, stdout) => {
|
|
579
640
|
if (!error) {
|
|
580
|
-
|
|
641
|
+
const lines = blkStdoutToObject(stdout).split('\n');
|
|
581
642
|
data = parseBlk(lines);
|
|
582
643
|
data = raidMatchLinux(data);
|
|
583
644
|
data = matchDevicesLinux(data);
|
|
@@ -586,9 +647,9 @@ function blockDevices(callback) {
|
|
|
586
647
|
}
|
|
587
648
|
resolve(data);
|
|
588
649
|
} else {
|
|
589
|
-
const procLsblk2 = exec('lsblk -bPo NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,UUID,ROTA,RO,RM,LABEL,MODEL,OWNER 2>/dev/null', { maxBuffer: 1024 * 1024 },
|
|
650
|
+
const procLsblk2 = exec('lsblk -bPo NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,UUID,ROTA,RO,RM,LABEL,MODEL,OWNER 2>/dev/null', { maxBuffer: 1024 * 1024 }, (error, stdout) => {
|
|
590
651
|
if (!error) {
|
|
591
|
-
|
|
652
|
+
const lines = blkStdoutToObject(stdout).split('\n');
|
|
592
653
|
data = parseBlk(lines);
|
|
593
654
|
data = raidMatchLinux(data);
|
|
594
655
|
}
|
|
@@ -597,7 +658,7 @@ function blockDevices(callback) {
|
|
|
597
658
|
}
|
|
598
659
|
resolve(data);
|
|
599
660
|
});
|
|
600
|
-
procLsblk2.on('error',
|
|
661
|
+
procLsblk2.on('error', () => {
|
|
601
662
|
if (callback) {
|
|
602
663
|
callback(data);
|
|
603
664
|
}
|
|
@@ -605,7 +666,7 @@ function blockDevices(callback) {
|
|
|
605
666
|
});
|
|
606
667
|
}
|
|
607
668
|
});
|
|
608
|
-
procLsblk1.on('error',
|
|
669
|
+
procLsblk1.on('error', () => {
|
|
609
670
|
if (callback) {
|
|
610
671
|
callback(data);
|
|
611
672
|
}
|
|
@@ -613,9 +674,9 @@ function blockDevices(callback) {
|
|
|
613
674
|
});
|
|
614
675
|
}
|
|
615
676
|
if (_darwin) {
|
|
616
|
-
const procDskutil = exec('diskutil info -all', { maxBuffer: 1024 * 1024 },
|
|
677
|
+
const procDskutil = exec('diskutil info -all', { maxBuffer: 1024 * 1024 }, (error, stdout) => {
|
|
617
678
|
if (!error) {
|
|
618
|
-
|
|
679
|
+
const lines = stdout.toString().split('\n');
|
|
619
680
|
// parse lines into temp array of devices
|
|
620
681
|
data = parseDevices(lines);
|
|
621
682
|
data = matchDevicesMac(data);
|
|
@@ -625,7 +686,7 @@ function blockDevices(callback) {
|
|
|
625
686
|
}
|
|
626
687
|
resolve(data);
|
|
627
688
|
});
|
|
628
|
-
procDskutil.on('error',
|
|
689
|
+
procDskutil.on('error', () => {
|
|
629
690
|
if (callback) {
|
|
630
691
|
callback(data);
|
|
631
692
|
}
|
|
@@ -633,25 +694,29 @@ function blockDevices(callback) {
|
|
|
633
694
|
});
|
|
634
695
|
}
|
|
635
696
|
if (_sunos) {
|
|
636
|
-
if (callback) {
|
|
697
|
+
if (callback) {
|
|
698
|
+
callback(data);
|
|
699
|
+
}
|
|
637
700
|
resolve(data);
|
|
638
701
|
}
|
|
639
702
|
if (_windows) {
|
|
640
|
-
|
|
703
|
+
const drivetypes = ['Unknown', 'NoRoot', 'Removable', 'Local', 'Network', 'CD/DVD', 'RAM'];
|
|
641
704
|
try {
|
|
642
705
|
// util.wmic('logicaldisk get Caption,Description,DeviceID,DriveType,FileSystem,FreeSpace,Name,Size,VolumeName,VolumeSerialNumber /value').then((stdout, error) => {
|
|
643
706
|
// util.powerShell('Get-CimInstance Win32_logicaldisk | select Caption,DriveType,Name,FileSystem,Size,VolumeSerialNumber,VolumeName | fl').then((stdout, error) => {
|
|
644
707
|
const workload = [];
|
|
645
708
|
workload.push(util.powerShell('Get-CimInstance -ClassName Win32_LogicalDisk | select Caption,DriveType,Name,FileSystem,Size,VolumeSerialNumber,VolumeName | fl'));
|
|
646
|
-
workload.push(
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
logicalDisks.
|
|
653
|
-
|
|
654
|
-
|
|
709
|
+
workload.push(
|
|
710
|
+
util.powerShell(
|
|
711
|
+
"Get-WmiObject -Class Win32_diskdrive | Select-Object -Property PNPDeviceId,DeviceID, Model, Size, @{L='Partitions'; E={$_.GetRelated('Win32_DiskPartition').GetRelated('Win32_LogicalDisk') | Select-Object -Property DeviceID, VolumeName, Size, FreeSpace}} | fl"
|
|
712
|
+
)
|
|
713
|
+
);
|
|
714
|
+
util.promiseAll(workload).then((res) => {
|
|
715
|
+
const logicalDisks = res.results[0].toString().split(/\n\s*\n/);
|
|
716
|
+
const diskDrives = res.results[1].toString().split(/\n\s*\n/);
|
|
717
|
+
logicalDisks.forEach((device) => {
|
|
718
|
+
const lines = device.split('\r\n');
|
|
719
|
+
const drivetype = util.getValue(lines, 'drivetype', ':');
|
|
655
720
|
if (drivetype) {
|
|
656
721
|
data.push({
|
|
657
722
|
name: util.getValue(lines, 'name', ':'),
|
|
@@ -660,7 +725,7 @@ function blockDevices(callback) {
|
|
|
660
725
|
fsType: util.getValue(lines, 'filesystem', ':').toLowerCase(),
|
|
661
726
|
mount: util.getValue(lines, 'caption', ':'),
|
|
662
727
|
size: util.getValue(lines, 'size', ':'),
|
|
663
|
-
physical:
|
|
728
|
+
physical: drivetype >= 0 && drivetype <= 6 ? drivetypes[drivetype] : drivetypes[0],
|
|
664
729
|
uuid: util.getValue(lines, 'volumeserialnumber', ':'),
|
|
665
730
|
label: util.getValue(lines, 'volumename', ':'),
|
|
666
731
|
model: '',
|
|
@@ -679,17 +744,20 @@ function blockDevices(callback) {
|
|
|
679
744
|
}
|
|
680
745
|
resolve(data);
|
|
681
746
|
});
|
|
682
|
-
} catch
|
|
683
|
-
if (callback) {
|
|
747
|
+
} catch {
|
|
748
|
+
if (callback) {
|
|
749
|
+
callback(data);
|
|
750
|
+
}
|
|
684
751
|
resolve(data);
|
|
685
752
|
}
|
|
686
753
|
}
|
|
687
754
|
if (_freebsd || _openbsd || _netbsd) {
|
|
688
755
|
// will follow
|
|
689
|
-
if (callback) {
|
|
756
|
+
if (callback) {
|
|
757
|
+
callback(null);
|
|
758
|
+
}
|
|
690
759
|
resolve(null);
|
|
691
760
|
}
|
|
692
|
-
|
|
693
761
|
});
|
|
694
762
|
});
|
|
695
763
|
}
|
|
@@ -700,7 +768,7 @@ exports.blockDevices = blockDevices;
|
|
|
700
768
|
// FS - speed
|
|
701
769
|
|
|
702
770
|
function calcFsSpeed(rx, wx) {
|
|
703
|
-
|
|
771
|
+
const result = {
|
|
704
772
|
rx: 0,
|
|
705
773
|
wx: 0,
|
|
706
774
|
tx: 0,
|
|
@@ -710,7 +778,7 @@ function calcFsSpeed(rx, wx) {
|
|
|
710
778
|
ms: 0
|
|
711
779
|
};
|
|
712
780
|
|
|
713
|
-
if (_fs_speed
|
|
781
|
+
if (_fs_speed?.ms) {
|
|
714
782
|
result.rx = rx;
|
|
715
783
|
result.wx = wx;
|
|
716
784
|
result.tx = result.rx + result.wx;
|
|
@@ -743,7 +811,6 @@ function calcFsSpeed(rx, wx) {
|
|
|
743
811
|
}
|
|
744
812
|
|
|
745
813
|
function fsStats(callback) {
|
|
746
|
-
|
|
747
814
|
return new Promise((resolve) => {
|
|
748
815
|
process.nextTick(() => {
|
|
749
816
|
if (_windows || _freebsd || _openbsd || _netbsd || _sunos) {
|
|
@@ -762,31 +829,33 @@ function fsStats(callback) {
|
|
|
762
829
|
|
|
763
830
|
let rx = 0;
|
|
764
831
|
let wx = 0;
|
|
765
|
-
if ((_fs_speed && !_fs_speed.ms) || (_fs_speed
|
|
832
|
+
if ((_fs_speed && !_fs_speed.ms) || (_fs_speed?.ms && Date.now() - _fs_speed.ms >= 500)) {
|
|
766
833
|
if (_linux) {
|
|
767
834
|
// exec("df -k | grep /dev/", function(error, stdout) {
|
|
768
|
-
const procLsblk = exec('lsblk -r 2>/dev/null | grep /', { maxBuffer: 1024 * 1024 },
|
|
835
|
+
const procLsblk = exec('lsblk -r 2>/dev/null | grep /', { maxBuffer: 1024 * 1024 }, (error, stdout) => {
|
|
769
836
|
if (!error) {
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
lines.forEach(
|
|
837
|
+
const lines = stdout.toString().split('\n');
|
|
838
|
+
const fs_filter = [];
|
|
839
|
+
lines.forEach((line) => {
|
|
773
840
|
if (line !== '') {
|
|
774
841
|
line = line.trim().split(' ');
|
|
775
|
-
if (fs_filter.indexOf(line[0]) === -1) {
|
|
842
|
+
if (fs_filter.indexOf(line[0]) === -1) {
|
|
843
|
+
fs_filter.push(line[0]);
|
|
844
|
+
}
|
|
776
845
|
}
|
|
777
846
|
});
|
|
778
847
|
|
|
779
|
-
|
|
780
|
-
const procCat = exec('cat /proc/diskstats | egrep "' + output + '"', { maxBuffer: 1024 * 1024 },
|
|
848
|
+
const output = fs_filter.join('|');
|
|
849
|
+
const procCat = exec('cat /proc/diskstats | egrep "' + output + '"', { maxBuffer: 1024 * 1024 }, (error, stdout) => {
|
|
781
850
|
if (!error) {
|
|
782
|
-
|
|
783
|
-
lines.forEach(
|
|
851
|
+
const lines = stdout.toString().split('\n');
|
|
852
|
+
lines.forEach((line) => {
|
|
784
853
|
line = line.trim();
|
|
785
854
|
if (line !== '') {
|
|
786
855
|
line = line.replace(/ +/g, ' ').split(' ');
|
|
787
856
|
|
|
788
|
-
rx += parseInt(line[5]) * 512;
|
|
789
|
-
wx += parseInt(line[9]) * 512;
|
|
857
|
+
rx += parseInt(line[5], 10) * 512;
|
|
858
|
+
wx += parseInt(line[9], 10) * 512;
|
|
790
859
|
}
|
|
791
860
|
});
|
|
792
861
|
result = calcFsSpeed(rx, wx);
|
|
@@ -796,7 +865,7 @@ function fsStats(callback) {
|
|
|
796
865
|
}
|
|
797
866
|
resolve(result);
|
|
798
867
|
});
|
|
799
|
-
procCat.on('error',
|
|
868
|
+
procCat.on('error', () => {
|
|
800
869
|
if (callback) {
|
|
801
870
|
callback(result);
|
|
802
871
|
}
|
|
@@ -809,35 +878,38 @@ function fsStats(callback) {
|
|
|
809
878
|
resolve(result);
|
|
810
879
|
}
|
|
811
880
|
});
|
|
812
|
-
procLsblk.on('error',
|
|
881
|
+
procLsblk.on('error', () => {
|
|
813
882
|
if (callback) {
|
|
814
883
|
callback(result);
|
|
815
884
|
}
|
|
816
885
|
resolve(result);
|
|
817
886
|
});
|
|
818
|
-
|
|
819
887
|
}
|
|
820
888
|
if (_darwin) {
|
|
821
|
-
const procIoreg = exec(
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
889
|
+
const procIoreg = exec(
|
|
890
|
+
'ioreg -c IOBlockStorageDriver -k Statistics -r -w0 | sed -n "/IOBlockStorageDriver/,/Statistics/p" | grep "Statistics" | tr -cd "01234567890,\n"',
|
|
891
|
+
{ maxBuffer: 1024 * 1024 },
|
|
892
|
+
(error, stdout) => {
|
|
893
|
+
if (!error) {
|
|
894
|
+
const lines = stdout.toString().split('\n');
|
|
895
|
+
lines.forEach((line) => {
|
|
896
|
+
line = line.trim();
|
|
897
|
+
if (line !== '') {
|
|
898
|
+
line = line.split(',');
|
|
899
|
+
|
|
900
|
+
rx += parseInt(line[2], 10);
|
|
901
|
+
wx += parseInt(line[9], 10);
|
|
902
|
+
}
|
|
903
|
+
});
|
|
904
|
+
result = calcFsSpeed(rx, wx);
|
|
905
|
+
}
|
|
906
|
+
if (callback) {
|
|
907
|
+
callback(result);
|
|
908
|
+
}
|
|
909
|
+
resolve(result);
|
|
837
910
|
}
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
procIoreg.on('error', function () {
|
|
911
|
+
);
|
|
912
|
+
procIoreg.on('error', () => {
|
|
841
913
|
if (callback) {
|
|
842
914
|
callback(result);
|
|
843
915
|
}
|
|
@@ -864,7 +936,7 @@ function fsStats(callback) {
|
|
|
864
936
|
exports.fsStats = fsStats;
|
|
865
937
|
|
|
866
938
|
function calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime) {
|
|
867
|
-
|
|
939
|
+
const result = {
|
|
868
940
|
rIO: 0,
|
|
869
941
|
wIO: 0,
|
|
870
942
|
tIO: 0,
|
|
@@ -879,7 +951,7 @@ function calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime) {
|
|
|
879
951
|
tWaitPercent: null,
|
|
880
952
|
ms: 0
|
|
881
953
|
};
|
|
882
|
-
if (_disk_io
|
|
954
|
+
if (_disk_io?.ms) {
|
|
883
955
|
result.rIO = rIO;
|
|
884
956
|
result.wIO = wIO;
|
|
885
957
|
result.tIO = rIO + wIO;
|
|
@@ -890,9 +962,9 @@ function calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime) {
|
|
|
890
962
|
result.rWaitTime = rWaitTime;
|
|
891
963
|
result.wWaitTime = wWaitTime;
|
|
892
964
|
result.tWaitTime = tWaitTime;
|
|
893
|
-
result.rWaitPercent = (result.rWaitTime - _disk_io.rWaitTime) * 100 /
|
|
894
|
-
result.wWaitPercent = (result.wWaitTime - _disk_io.wWaitTime) * 100 /
|
|
895
|
-
result.tWaitPercent = (result.tWaitTime - _disk_io.tWaitTime) * 100 /
|
|
965
|
+
result.rWaitPercent = ((result.rWaitTime - _disk_io.rWaitTime) * 100) / result.ms;
|
|
966
|
+
result.wWaitPercent = ((result.wWaitTime - _disk_io.wWaitTime) * 100) / result.ms;
|
|
967
|
+
result.tWaitPercent = ((result.tWaitTime - _disk_io.tWaitTime) * 100) / result.ms;
|
|
896
968
|
_disk_io.rIO = rIO;
|
|
897
969
|
_disk_io.wIO = wIO;
|
|
898
970
|
_disk_io.rIO_sec = result.rIO_sec;
|
|
@@ -931,7 +1003,6 @@ function calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime) {
|
|
|
931
1003
|
}
|
|
932
1004
|
|
|
933
1005
|
function disksIO(callback) {
|
|
934
|
-
|
|
935
1006
|
return new Promise((resolve) => {
|
|
936
1007
|
process.nextTick(() => {
|
|
937
1008
|
if (_windows) {
|
|
@@ -962,27 +1033,30 @@ function disksIO(callback) {
|
|
|
962
1033
|
let wWaitTime = 0;
|
|
963
1034
|
let tWaitTime = 0;
|
|
964
1035
|
|
|
965
|
-
if ((_disk_io && !_disk_io.ms) || (_disk_io
|
|
1036
|
+
if ((_disk_io && !_disk_io.ms) || (_disk_io?.ms && Date.now() - _disk_io.ms >= 500)) {
|
|
966
1037
|
if (_linux || _freebsd || _openbsd || _netbsd) {
|
|
967
1038
|
// prints Block layer statistics for all mounted volumes
|
|
968
1039
|
// var cmd = "for mount in `lsblk | grep / | sed -r 's/│ └─//' | cut -d ' ' -f 1`; do cat /sys/block/$mount/stat | sed -r 's/ +/;/g' | sed -r 's/^;//'; done";
|
|
969
1040
|
// var cmd = "for mount in `lsblk | grep / | sed 's/[│└─├]//g' | awk '{$1=$1};1' | cut -d ' ' -f 1 | sort -u`; do cat /sys/block/$mount/stat | sed -r 's/ +/;/g' | sed -r 's/^;//'; done";
|
|
970
|
-
|
|
1041
|
+
const cmd =
|
|
1042
|
+
'for mount in `lsblk 2>/dev/null | grep " disk " | sed "s/[│└─├]//g" | awk \'{$1=$1};1\' | cut -d " " -f 1 | sort -u`; do cat /sys/block/$mount/stat | sed -r "s/ +/;/g" | sed -r "s/^;//"; done';
|
|
971
1043
|
|
|
972
|
-
exec(cmd, { maxBuffer: 1024 * 1024 },
|
|
1044
|
+
exec(cmd, { maxBuffer: 1024 * 1024 }, (error, stdout) => {
|
|
973
1045
|
if (!error) {
|
|
974
|
-
|
|
975
|
-
lines.forEach(
|
|
1046
|
+
const lines = stdout.split('\n');
|
|
1047
|
+
lines.forEach((line) => {
|
|
976
1048
|
// ignore empty lines
|
|
977
|
-
if (!line) {
|
|
1049
|
+
if (!line) {
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
978
1052
|
|
|
979
1053
|
// sum r/wIO of all disks to compute all disks IO
|
|
980
|
-
|
|
981
|
-
rIO += parseInt(stats[0]);
|
|
982
|
-
wIO += parseInt(stats[4]);
|
|
983
|
-
rWaitTime += parseInt(stats[3]);
|
|
984
|
-
wWaitTime += parseInt(stats[7]);
|
|
985
|
-
tWaitTime += parseInt(stats[10]);
|
|
1054
|
+
const stats = line.split(';');
|
|
1055
|
+
rIO += parseInt(stats[0], 10);
|
|
1056
|
+
wIO += parseInt(stats[4], 10);
|
|
1057
|
+
rWaitTime += parseInt(stats[3], 10);
|
|
1058
|
+
wWaitTime += parseInt(stats[7], 10);
|
|
1059
|
+
tWaitTime += parseInt(stats[10], 10);
|
|
986
1060
|
});
|
|
987
1061
|
result = calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime);
|
|
988
1062
|
|
|
@@ -999,25 +1073,29 @@ function disksIO(callback) {
|
|
|
999
1073
|
});
|
|
1000
1074
|
}
|
|
1001
1075
|
if (_darwin) {
|
|
1002
|
-
exec(
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1076
|
+
exec(
|
|
1077
|
+
'ioreg -c IOBlockStorageDriver -k Statistics -r -w0 | sed -n "/IOBlockStorageDriver/,/Statistics/p" | grep "Statistics" | tr -cd "01234567890,\n"',
|
|
1078
|
+
{ maxBuffer: 1024 * 1024 },
|
|
1079
|
+
(error, stdout) => {
|
|
1080
|
+
if (!error) {
|
|
1081
|
+
const lines = stdout.toString().split('\n');
|
|
1082
|
+
lines.forEach((line) => {
|
|
1083
|
+
line = line.trim();
|
|
1084
|
+
if (line !== '') {
|
|
1085
|
+
line = line.split(',');
|
|
1086
|
+
|
|
1087
|
+
rIO += parseInt(line[10], 10);
|
|
1088
|
+
wIO += parseInt(line[0], 10);
|
|
1089
|
+
}
|
|
1090
|
+
});
|
|
1091
|
+
result = calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime);
|
|
1092
|
+
}
|
|
1093
|
+
if (callback) {
|
|
1094
|
+
callback(result);
|
|
1095
|
+
}
|
|
1096
|
+
resolve(result);
|
|
1018
1097
|
}
|
|
1019
|
-
|
|
1020
|
-
});
|
|
1098
|
+
);
|
|
1021
1099
|
}
|
|
1022
1100
|
} else {
|
|
1023
1101
|
result.rIO = _disk_io.rIO;
|
|
@@ -1045,7 +1123,6 @@ function disksIO(callback) {
|
|
|
1045
1123
|
exports.disksIO = disksIO;
|
|
1046
1124
|
|
|
1047
1125
|
function diskLayout(callback) {
|
|
1048
|
-
|
|
1049
1126
|
function getVendorFromModel(model) {
|
|
1050
1127
|
const diskManufacturers = [
|
|
1051
1128
|
{ pattern: 'WESTERN.*', manufacturer: 'Western Digital' },
|
|
@@ -1078,7 +1155,7 @@ function diskLayout(callback) {
|
|
|
1078
1155
|
{ pattern: 'ECM.*', manufacturer: 'ECM' },
|
|
1079
1156
|
{ pattern: 'INTEL.*', manufacturer: 'INTEL' },
|
|
1080
1157
|
{ pattern: 'EVO.*', manufacturer: 'Samsung' },
|
|
1081
|
-
{ pattern: 'APPLE.*', manufacturer: 'Apple' }
|
|
1158
|
+
{ pattern: 'APPLE.*', manufacturer: 'Apple' }
|
|
1082
1159
|
];
|
|
1083
1160
|
|
|
1084
1161
|
let result = '';
|
|
@@ -1086,7 +1163,9 @@ function diskLayout(callback) {
|
|
|
1086
1163
|
model = model.toUpperCase();
|
|
1087
1164
|
diskManufacturers.forEach((manufacturer) => {
|
|
1088
1165
|
const re = RegExp(manufacturer.pattern);
|
|
1089
|
-
if (re.test(model)) {
|
|
1166
|
+
if (re.test(model)) {
|
|
1167
|
+
result = manufacturer.manufacturer;
|
|
1168
|
+
}
|
|
1090
1169
|
});
|
|
1091
1170
|
}
|
|
1092
1171
|
return result;
|
|
@@ -1094,8 +1173,7 @@ function diskLayout(callback) {
|
|
|
1094
1173
|
|
|
1095
1174
|
return new Promise((resolve) => {
|
|
1096
1175
|
process.nextTick(() => {
|
|
1097
|
-
|
|
1098
|
-
const commitResult = res => {
|
|
1176
|
+
const commitResult = (res) => {
|
|
1099
1177
|
for (let i = 0; i < res.length; i++) {
|
|
1100
1178
|
delete res[i].BSDName;
|
|
1101
1179
|
}
|
|
@@ -1105,13 +1183,13 @@ function diskLayout(callback) {
|
|
|
1105
1183
|
resolve(res);
|
|
1106
1184
|
};
|
|
1107
1185
|
|
|
1108
|
-
|
|
1186
|
+
const result = [];
|
|
1109
1187
|
let cmd = '';
|
|
1110
1188
|
|
|
1111
1189
|
if (_linux) {
|
|
1112
1190
|
let cmdFullSmart = '';
|
|
1113
1191
|
|
|
1114
|
-
exec('export LC_ALL=C; lsblk -ablJO 2>/dev/null; unset LC_ALL', { maxBuffer: 1024 * 1024 },
|
|
1192
|
+
exec('export LC_ALL=C; lsblk -ablJO 2>/dev/null; unset LC_ALL', { maxBuffer: 1024 * 1024 }, (error, stdout) => {
|
|
1115
1193
|
if (!error) {
|
|
1116
1194
|
try {
|
|
1117
1195
|
const out = stdout.toString().trim();
|
|
@@ -1119,16 +1197,36 @@ function diskLayout(callback) {
|
|
|
1119
1197
|
try {
|
|
1120
1198
|
const outJSON = JSON.parse(out);
|
|
1121
1199
|
if (outJSON && {}.hasOwnProperty.call(outJSON, 'blockdevices')) {
|
|
1122
|
-
devices = outJSON.blockdevices.filter(item => {
|
|
1200
|
+
devices = outJSON.blockdevices.filter((item) => {
|
|
1201
|
+
return (
|
|
1202
|
+
item.type === 'disk' &&
|
|
1203
|
+
item.size > 0 &&
|
|
1204
|
+
(item.model !== null ||
|
|
1205
|
+
(item.mountpoint === null &&
|
|
1206
|
+
item.label === null &&
|
|
1207
|
+
item.fstype === null &&
|
|
1208
|
+
item.parttype === null &&
|
|
1209
|
+
item.path &&
|
|
1210
|
+
item.path.indexOf('/ram') !== 0 &&
|
|
1211
|
+
item.path.indexOf('/loop') !== 0 &&
|
|
1212
|
+
item['disc-max'] &&
|
|
1213
|
+
item['disc-max'] !== 0))
|
|
1214
|
+
);
|
|
1215
|
+
});
|
|
1123
1216
|
}
|
|
1124
|
-
} catch
|
|
1217
|
+
} catch {
|
|
1125
1218
|
// fallback to older version of lsblk
|
|
1126
1219
|
try {
|
|
1127
|
-
const out2 = execSync(
|
|
1128
|
-
|
|
1220
|
+
const out2 = execSync(
|
|
1221
|
+
'export LC_ALL=C; lsblk -bPo NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,UUID,ROTA,RO,RM,LABEL,MODEL,OWNER,GROUP 2>/dev/null; unset LC_ALL',
|
|
1222
|
+
util.execOptsLinux
|
|
1223
|
+
).toString();
|
|
1224
|
+
const lines = blkStdoutToObject(out2).split('\n');
|
|
1129
1225
|
const data = parseBlk(lines);
|
|
1130
|
-
devices = data.filter(item => {
|
|
1131
|
-
|
|
1226
|
+
devices = data.filter((item) => {
|
|
1227
|
+
return item.type === 'disk' && item.size > 0 && ((item.model !== null && item.model !== '') || (item.mount === '' && item.label === '' && item.fsType === ''));
|
|
1228
|
+
});
|
|
1229
|
+
} catch {
|
|
1132
1230
|
util.noop();
|
|
1133
1231
|
}
|
|
1134
1232
|
}
|
|
@@ -1137,8 +1235,10 @@ function diskLayout(callback) {
|
|
|
1137
1235
|
const BSDName = '/dev/' + device.name;
|
|
1138
1236
|
const logical = device.name;
|
|
1139
1237
|
try {
|
|
1140
|
-
mediumType = execSync('cat /sys/block/' + logical + '/queue/rotational 2>/dev/null', util.execOptsLinux)
|
|
1141
|
-
|
|
1238
|
+
mediumType = execSync('cat /sys/block/' + logical + '/queue/rotational 2>/dev/null', util.execOptsLinux)
|
|
1239
|
+
.toString()
|
|
1240
|
+
.split('\n')[0];
|
|
1241
|
+
} catch {
|
|
1142
1242
|
util.noop();
|
|
1143
1243
|
}
|
|
1144
1244
|
let interfaceType = device.tran ? device.tran.toUpperCase().trim() : '';
|
|
@@ -1148,7 +1248,18 @@ function diskLayout(callback) {
|
|
|
1148
1248
|
}
|
|
1149
1249
|
result.push({
|
|
1150
1250
|
device: BSDName,
|
|
1151
|
-
type:
|
|
1251
|
+
type:
|
|
1252
|
+
mediumType === '0'
|
|
1253
|
+
? 'SSD'
|
|
1254
|
+
: mediumType === '1'
|
|
1255
|
+
? 'HD'
|
|
1256
|
+
: mediumType === '2'
|
|
1257
|
+
? 'NVMe'
|
|
1258
|
+
: device.model && device.model.indexOf('SSD') > -1
|
|
1259
|
+
? 'SSD'
|
|
1260
|
+
: device.model && device.model.indexOf('NVM') > -1
|
|
1261
|
+
? 'NVMe'
|
|
1262
|
+
: 'HD',
|
|
1152
1263
|
name: device.model || '',
|
|
1153
1264
|
vendor: getVendorFromModel(device.model) || (device.vendor ? device.vendor.trim() : ''),
|
|
1154
1265
|
size: device.size || 0,
|
|
@@ -1169,22 +1280,22 @@ function diskLayout(callback) {
|
|
|
1169
1280
|
cmd += `printf "\n${BSDName}|"; smartctl -H ${BSDName} | grep overall;`;
|
|
1170
1281
|
cmdFullSmart += `${cmdFullSmart ? 'printf ",";' : ''}smartctl -a -j ${BSDName};`;
|
|
1171
1282
|
});
|
|
1172
|
-
} catch
|
|
1283
|
+
} catch {
|
|
1173
1284
|
util.noop();
|
|
1174
1285
|
}
|
|
1175
1286
|
}
|
|
1176
1287
|
// check S.M.A.R.T. status
|
|
1177
1288
|
if (cmdFullSmart) {
|
|
1178
|
-
exec(cmdFullSmart, { maxBuffer: 1024 * 1024 },
|
|
1289
|
+
exec(cmdFullSmart, { maxBuffer: 1024 * 1024 }, (error, stdout) => {
|
|
1179
1290
|
try {
|
|
1180
1291
|
const data = JSON.parse(`[${stdout}]`);
|
|
1181
|
-
data.forEach(disk => {
|
|
1292
|
+
data.forEach((disk) => {
|
|
1182
1293
|
const diskBSDName = disk.smartctl.argv[disk.smartctl.argv.length - 1];
|
|
1183
1294
|
|
|
1184
1295
|
for (let i = 0; i < result.length; i++) {
|
|
1185
1296
|
if (result[i].BSDName === diskBSDName) {
|
|
1186
|
-
result[i].smartStatus =
|
|
1187
|
-
if (disk.temperature
|
|
1297
|
+
result[i].smartStatus = disk.smart_status.passed ? 'Ok' : disk.smart_status.passed === false ? 'Predicted Failure' : 'unknown';
|
|
1298
|
+
if (disk.temperature?.current) {
|
|
1188
1299
|
result[i].temperature = disk.temperature.current;
|
|
1189
1300
|
}
|
|
1190
1301
|
result[i].smartData = disk;
|
|
@@ -1192,24 +1303,24 @@ function diskLayout(callback) {
|
|
|
1192
1303
|
}
|
|
1193
1304
|
});
|
|
1194
1305
|
commitResult(result);
|
|
1195
|
-
} catch
|
|
1306
|
+
} catch {
|
|
1196
1307
|
if (cmd) {
|
|
1197
1308
|
cmd = cmd + 'printf "\n"';
|
|
1198
|
-
exec(cmd, { maxBuffer: 1024 * 1024 },
|
|
1199
|
-
|
|
1200
|
-
lines.forEach(line => {
|
|
1309
|
+
exec(cmd, { maxBuffer: 1024 * 1024 }, (error, stdout) => {
|
|
1310
|
+
const lines = stdout.toString().split('\n');
|
|
1311
|
+
lines.forEach((line) => {
|
|
1201
1312
|
if (line) {
|
|
1202
|
-
|
|
1313
|
+
const parts = line.split('|');
|
|
1203
1314
|
if (parts.length === 2) {
|
|
1204
|
-
|
|
1315
|
+
const BSDName = parts[0];
|
|
1205
1316
|
parts[1] = parts[1].trim();
|
|
1206
|
-
|
|
1317
|
+
const parts2 = parts[1].split(':');
|
|
1207
1318
|
if (parts2.length === 2) {
|
|
1208
1319
|
parts2[1] = parts2[1].trim();
|
|
1209
|
-
|
|
1320
|
+
const status = parts2[1].toLowerCase();
|
|
1210
1321
|
for (let i = 0; i < result.length; i++) {
|
|
1211
1322
|
if (result[i].BSDName === BSDName) {
|
|
1212
|
-
result[i].smartStatus =
|
|
1323
|
+
result[i].smartStatus = status === 'passed' ? 'Ok' : status === 'failed!' ? 'Predicted Failure' : 'unknown';
|
|
1213
1324
|
}
|
|
1214
1325
|
}
|
|
1215
1326
|
}
|
|
@@ -1229,47 +1340,65 @@ function diskLayout(callback) {
|
|
|
1229
1340
|
});
|
|
1230
1341
|
}
|
|
1231
1342
|
if (_freebsd || _openbsd || _netbsd) {
|
|
1232
|
-
if (callback) {
|
|
1343
|
+
if (callback) {
|
|
1344
|
+
callback(result);
|
|
1345
|
+
}
|
|
1233
1346
|
resolve(result);
|
|
1234
1347
|
}
|
|
1235
1348
|
if (_sunos) {
|
|
1236
|
-
if (callback) {
|
|
1349
|
+
if (callback) {
|
|
1350
|
+
callback(result);
|
|
1351
|
+
}
|
|
1237
1352
|
resolve(result);
|
|
1238
1353
|
}
|
|
1239
1354
|
if (_darwin) {
|
|
1240
|
-
exec('system_profiler SPSerialATADataType SPNVMeDataType SPUSBDataType', { maxBuffer: 1024 * 1024 },
|
|
1355
|
+
exec('system_profiler SPSerialATADataType SPNVMeDataType SPUSBDataType', { maxBuffer: 1024 * 1024 }, (error, stdout) => {
|
|
1241
1356
|
if (!error) {
|
|
1242
1357
|
// split by type:
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1358
|
+
const lines = stdout.toString().split('\n');
|
|
1359
|
+
const linesSATA = [];
|
|
1360
|
+
const linesNVMe = [];
|
|
1361
|
+
const linesUSB = [];
|
|
1247
1362
|
let dataType = 'SATA';
|
|
1248
|
-
lines.forEach(line => {
|
|
1249
|
-
if (line === 'NVMExpress:') {
|
|
1250
|
-
|
|
1251
|
-
else if (line === '
|
|
1252
|
-
|
|
1253
|
-
else if (
|
|
1254
|
-
|
|
1363
|
+
lines.forEach((line) => {
|
|
1364
|
+
if (line === 'NVMExpress:') {
|
|
1365
|
+
dataType = 'NVMe';
|
|
1366
|
+
} else if (line === 'USB:') {
|
|
1367
|
+
dataType = 'USB';
|
|
1368
|
+
} else if (line === 'SATA/SATA Express:') {
|
|
1369
|
+
dataType = 'SATA';
|
|
1370
|
+
} else if (dataType === 'SATA') {
|
|
1371
|
+
linesSATA.push(line);
|
|
1372
|
+
} else if (dataType === 'NVMe') {
|
|
1373
|
+
linesNVMe.push(line);
|
|
1374
|
+
} else if (dataType === 'USB') {
|
|
1375
|
+
linesUSB.push(line);
|
|
1376
|
+
}
|
|
1255
1377
|
});
|
|
1256
1378
|
try {
|
|
1257
1379
|
// Serial ATA Drives
|
|
1258
|
-
|
|
1380
|
+
const devices = linesSATA.join('\n').split(' Physical Interconnect: ');
|
|
1259
1381
|
devices.shift();
|
|
1260
|
-
devices.forEach(
|
|
1382
|
+
devices.forEach((device) => {
|
|
1261
1383
|
device = 'InterfaceType: ' + device;
|
|
1262
|
-
|
|
1384
|
+
const lines = device.split('\n');
|
|
1263
1385
|
const mediumType = util.getValue(lines, 'Medium Type', ':', true).trim();
|
|
1264
1386
|
const sizeStr = util.getValue(lines, 'capacity', ':', true).trim();
|
|
1265
1387
|
const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim();
|
|
1266
1388
|
if (sizeStr) {
|
|
1267
1389
|
let sizeValue = 0;
|
|
1268
1390
|
if (sizeStr.indexOf('(') >= 0) {
|
|
1269
|
-
sizeValue = parseInt(
|
|
1391
|
+
sizeValue = parseInt(
|
|
1392
|
+
sizeStr
|
|
1393
|
+
.match(/\(([^)]+)\)/)[1]
|
|
1394
|
+
.replace(/\./g, '')
|
|
1395
|
+
.replace(/,/g, '')
|
|
1396
|
+
.replace(/\s/g, ''),
|
|
1397
|
+
10
|
|
1398
|
+
);
|
|
1270
1399
|
}
|
|
1271
1400
|
if (!sizeValue) {
|
|
1272
|
-
sizeValue = parseInt(sizeStr);
|
|
1401
|
+
sizeValue = parseInt(sizeStr, 10);
|
|
1273
1402
|
}
|
|
1274
1403
|
if (sizeValue) {
|
|
1275
1404
|
const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase();
|
|
@@ -1297,27 +1426,34 @@ function diskLayout(callback) {
|
|
|
1297
1426
|
}
|
|
1298
1427
|
}
|
|
1299
1428
|
});
|
|
1300
|
-
} catch
|
|
1429
|
+
} catch {
|
|
1301
1430
|
util.noop();
|
|
1302
1431
|
}
|
|
1303
1432
|
|
|
1304
1433
|
// NVME Drives
|
|
1305
1434
|
try {
|
|
1306
|
-
|
|
1435
|
+
const devices = linesNVMe.join('\n').split('\n\n Capacity:');
|
|
1307
1436
|
devices.shift();
|
|
1308
|
-
devices.forEach(
|
|
1309
|
-
device =
|
|
1310
|
-
|
|
1437
|
+
devices.forEach((device) => {
|
|
1438
|
+
device = `!Capacity: ${device}`;
|
|
1439
|
+
const lines = device.split('\n');
|
|
1311
1440
|
const linkWidth = util.getValue(lines, 'link width', ':', true).trim();
|
|
1312
1441
|
const sizeStr = util.getValue(lines, '!capacity', ':', true).trim();
|
|
1313
1442
|
const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim();
|
|
1314
1443
|
if (sizeStr) {
|
|
1315
1444
|
let sizeValue = 0;
|
|
1316
1445
|
if (sizeStr.indexOf('(') >= 0) {
|
|
1317
|
-
sizeValue = parseInt(
|
|
1446
|
+
sizeValue = parseInt(
|
|
1447
|
+
sizeStr
|
|
1448
|
+
.match(/\(([^)]+)\)/)[1]
|
|
1449
|
+
.replace(/\./g, '')
|
|
1450
|
+
.replace(/,/g, '')
|
|
1451
|
+
.replace(/\s/g, ''),
|
|
1452
|
+
10
|
|
1453
|
+
);
|
|
1318
1454
|
}
|
|
1319
1455
|
if (!sizeValue) {
|
|
1320
|
-
sizeValue = parseInt(sizeStr);
|
|
1456
|
+
sizeValue = parseInt(sizeStr, 10);
|
|
1321
1457
|
}
|
|
1322
1458
|
if (sizeValue) {
|
|
1323
1459
|
const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase();
|
|
@@ -1341,28 +1477,35 @@ function diskLayout(callback) {
|
|
|
1341
1477
|
temperature: null,
|
|
1342
1478
|
BSDName: BSDName
|
|
1343
1479
|
});
|
|
1344
|
-
cmd = cmd
|
|
1480
|
+
cmd = `${cmd}printf "\n${BSDName}|"; diskutil info /dev/${BSDName} | grep SMART;`;
|
|
1345
1481
|
}
|
|
1346
1482
|
}
|
|
1347
1483
|
});
|
|
1348
|
-
} catch
|
|
1484
|
+
} catch {
|
|
1349
1485
|
util.noop();
|
|
1350
1486
|
}
|
|
1351
1487
|
// USB Drives
|
|
1352
1488
|
try {
|
|
1353
|
-
|
|
1489
|
+
const devices = linesUSB.join('\n').replaceAll('Media:\n ', 'Model:').split('\n\n Product ID:');
|
|
1354
1490
|
devices.shift();
|
|
1355
|
-
devices.forEach(
|
|
1356
|
-
|
|
1491
|
+
devices.forEach((device) => {
|
|
1492
|
+
const lines = device.split('\n');
|
|
1357
1493
|
const sizeStr = util.getValue(lines, 'Capacity', ':', true).trim();
|
|
1358
1494
|
const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim();
|
|
1359
1495
|
if (sizeStr) {
|
|
1360
1496
|
let sizeValue = 0;
|
|
1361
1497
|
if (sizeStr.indexOf('(') >= 0) {
|
|
1362
|
-
sizeValue = parseInt(
|
|
1498
|
+
sizeValue = parseInt(
|
|
1499
|
+
sizeStr
|
|
1500
|
+
.match(/\(([^)]+)\)/)[1]
|
|
1501
|
+
.replace(/\./g, '')
|
|
1502
|
+
.replace(/,/g, '')
|
|
1503
|
+
.replace(/\s/g, ''),
|
|
1504
|
+
10
|
|
1505
|
+
);
|
|
1363
1506
|
}
|
|
1364
1507
|
if (!sizeValue) {
|
|
1365
|
-
sizeValue = parseInt(sizeStr);
|
|
1508
|
+
sizeValue = parseInt(sizeStr, 10);
|
|
1366
1509
|
}
|
|
1367
1510
|
if (sizeValue) {
|
|
1368
1511
|
const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase();
|
|
@@ -1390,26 +1533,26 @@ function diskLayout(callback) {
|
|
|
1390
1533
|
}
|
|
1391
1534
|
}
|
|
1392
1535
|
});
|
|
1393
|
-
} catch
|
|
1536
|
+
} catch {
|
|
1394
1537
|
util.noop();
|
|
1395
1538
|
}
|
|
1396
1539
|
if (cmd) {
|
|
1397
1540
|
cmd = cmd + 'printf "\n"';
|
|
1398
|
-
exec(cmd, { maxBuffer: 1024 * 1024 },
|
|
1399
|
-
|
|
1400
|
-
lines.forEach(line => {
|
|
1541
|
+
exec(cmd, { maxBuffer: 1024 * 1024 }, (error, stdout) => {
|
|
1542
|
+
const lines = stdout.toString().split('\n');
|
|
1543
|
+
lines.forEach((line) => {
|
|
1401
1544
|
if (line) {
|
|
1402
|
-
|
|
1545
|
+
const parts = line.split('|');
|
|
1403
1546
|
if (parts.length === 2) {
|
|
1404
|
-
|
|
1547
|
+
const BSDName = parts[0];
|
|
1405
1548
|
parts[1] = parts[1].trim();
|
|
1406
|
-
|
|
1549
|
+
const parts2 = parts[1].split(':');
|
|
1407
1550
|
if (parts2.length === 2) {
|
|
1408
1551
|
parts2[1] = parts2[1].trim();
|
|
1409
|
-
|
|
1552
|
+
const status = parts2[1].toLowerCase();
|
|
1410
1553
|
for (let i = 0; i < result.length; i++) {
|
|
1411
1554
|
if (result[i].BSDName === BSDName) {
|
|
1412
|
-
result[i].smartStatus =
|
|
1555
|
+
result[i].smartStatus = status === 'not supported' ? 'not supported' : status === 'verified' ? 'Ok' : status === 'failing' ? 'Predicted Failure' : 'unknown';
|
|
1413
1556
|
}
|
|
1414
1557
|
}
|
|
1415
1558
|
}
|
|
@@ -1429,69 +1572,79 @@ function diskLayout(callback) {
|
|
|
1429
1572
|
if (_windows) {
|
|
1430
1573
|
try {
|
|
1431
1574
|
const workload = [];
|
|
1432
|
-
workload.push(
|
|
1575
|
+
workload.push(
|
|
1576
|
+
util.powerShell(
|
|
1577
|
+
'Get-CimInstance Win32_DiskDrive | select Caption,Size,Status,PNPDeviceId,DeviceId,BytesPerSector,TotalCylinders,TotalHeads,TotalSectors,TotalTracks,TracksPerCylinder,SectorsPerTrack,FirmwareRevision,SerialNumber,InterfaceType | fl'
|
|
1578
|
+
)
|
|
1579
|
+
);
|
|
1433
1580
|
workload.push(util.powerShell('Get-PhysicalDisk | select BusType,MediaType,FriendlyName,Model,SerialNumber,Size | fl'));
|
|
1434
1581
|
if (util.smartMonToolsInstalled()) {
|
|
1435
1582
|
try {
|
|
1436
1583
|
const smartDev = JSON.parse(execSync('smartctl --scan -j').toString());
|
|
1437
|
-
if (smartDev
|
|
1584
|
+
if (smartDev?.devices && smartDev.devices.length > 0) {
|
|
1438
1585
|
smartDev.devices.forEach((dev) => {
|
|
1439
1586
|
workload.push(execPromiseSave(`smartctl -j -a ${dev.name}`, util.execOptsWin));
|
|
1440
1587
|
});
|
|
1441
1588
|
}
|
|
1442
|
-
} catch
|
|
1589
|
+
} catch {
|
|
1443
1590
|
util.noop();
|
|
1444
1591
|
}
|
|
1445
1592
|
}
|
|
1446
|
-
util.promiseAll(
|
|
1447
|
-
workload
|
|
1448
|
-
).then((data) => {
|
|
1593
|
+
util.promiseAll(workload).then((data) => {
|
|
1449
1594
|
let devices = data.results[0].toString().split(/\n\s*\n/);
|
|
1450
|
-
devices.forEach(
|
|
1451
|
-
|
|
1595
|
+
devices.forEach((device) => {
|
|
1596
|
+
const lines = device.split('\r\n');
|
|
1452
1597
|
const size = util.getValue(lines, 'Size', ':').trim();
|
|
1453
1598
|
const status = util.getValue(lines, 'Status', ':').trim().toLowerCase();
|
|
1454
1599
|
if (size) {
|
|
1455
1600
|
result.push({
|
|
1456
|
-
device: util.getValue(lines, 'DeviceId', ':'),
|
|
1457
|
-
type: device.indexOf('SSD') > -1 ? 'SSD' : 'HD',
|
|
1601
|
+
device: util.getValue(lines, 'DeviceId', ':'), // changed from PNPDeviceId to DeviceID (be be able to match devices)
|
|
1602
|
+
type: device.indexOf('SSD') > -1 ? 'SSD' : 'HD', // just a starting point ... better: MSFT_PhysicalDisk - Media Type ... see below
|
|
1458
1603
|
name: util.getValue(lines, 'Caption', ':'),
|
|
1459
1604
|
vendor: getVendorFromModel(util.getValue(lines, 'Caption', ':', true).trim()),
|
|
1460
|
-
size: parseInt(size),
|
|
1461
|
-
bytesPerSector: parseInt(util.getValue(lines, 'BytesPerSector', ':')),
|
|
1462
|
-
totalCylinders: parseInt(util.getValue(lines, 'TotalCylinders', ':')),
|
|
1463
|
-
totalHeads: parseInt(util.getValue(lines, 'TotalHeads', ':')),
|
|
1464
|
-
totalSectors: parseInt(util.getValue(lines, 'TotalSectors', ':')),
|
|
1465
|
-
totalTracks: parseInt(util.getValue(lines, 'TotalTracks', ':')),
|
|
1466
|
-
tracksPerCylinder: parseInt(util.getValue(lines, 'TracksPerCylinder', ':')),
|
|
1467
|
-
sectorsPerTrack: parseInt(util.getValue(lines, 'SectorsPerTrack', ':')),
|
|
1605
|
+
size: parseInt(size, 10),
|
|
1606
|
+
bytesPerSector: parseInt(util.getValue(lines, 'BytesPerSector', ':'), 10),
|
|
1607
|
+
totalCylinders: parseInt(util.getValue(lines, 'TotalCylinders', ':'), 10),
|
|
1608
|
+
totalHeads: parseInt(util.getValue(lines, 'TotalHeads', ':'), 10),
|
|
1609
|
+
totalSectors: parseInt(util.getValue(lines, 'TotalSectors', ':'), 10),
|
|
1610
|
+
totalTracks: parseInt(util.getValue(lines, 'TotalTracks', ':'), 10),
|
|
1611
|
+
tracksPerCylinder: parseInt(util.getValue(lines, 'TracksPerCylinder', ':'), 10),
|
|
1612
|
+
sectorsPerTrack: parseInt(util.getValue(lines, 'SectorsPerTrack', ':'), 10),
|
|
1468
1613
|
firmwareRevision: util.getValue(lines, 'FirmwareRevision', ':').trim(),
|
|
1469
1614
|
serialNum: util.getValue(lines, 'SerialNumber', ':').trim(),
|
|
1470
1615
|
interfaceType: util.getValue(lines, 'InterfaceType', ':').trim(),
|
|
1471
|
-
smartStatus:
|
|
1472
|
-
temperature: null
|
|
1616
|
+
smartStatus: status === 'ok' ? 'Ok' : status === 'degraded' ? 'Degraded' : status === 'pred fail' ? 'Predicted Failure' : 'Unknown',
|
|
1617
|
+
temperature: null
|
|
1473
1618
|
});
|
|
1474
1619
|
}
|
|
1475
1620
|
});
|
|
1476
1621
|
devices = data.results[1].split(/\n\s*\n/);
|
|
1477
|
-
devices.forEach(
|
|
1478
|
-
|
|
1622
|
+
devices.forEach((device) => {
|
|
1623
|
+
const lines = device.split('\r\n');
|
|
1479
1624
|
const serialNum = util.getValue(lines, 'SerialNumber', ':').trim();
|
|
1480
1625
|
const name = util.getValue(lines, 'FriendlyName', ':').trim().replace('Msft ', 'Microsoft');
|
|
1481
1626
|
const size = util.getValue(lines, 'Size', ':').trim();
|
|
1482
1627
|
const model = util.getValue(lines, 'Model', ':').trim();
|
|
1483
1628
|
const interfaceType = util.getValue(lines, 'BusType', ':').trim();
|
|
1484
1629
|
let mediaType = util.getValue(lines, 'MediaType', ':').trim();
|
|
1485
|
-
if (mediaType === '3' || mediaType === 'HDD') {
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
if (mediaType === '
|
|
1630
|
+
if (mediaType === '3' || mediaType === 'HDD') {
|
|
1631
|
+
mediaType = 'HD';
|
|
1632
|
+
}
|
|
1633
|
+
if (mediaType === '4') {
|
|
1634
|
+
mediaType = 'SSD';
|
|
1635
|
+
}
|
|
1636
|
+
if (mediaType === '5') {
|
|
1637
|
+
mediaType = 'SCM';
|
|
1638
|
+
}
|
|
1639
|
+
if (mediaType === 'Unspecified' && (model.toLowerCase().indexOf('virtual') > -1 || model.toLowerCase().indexOf('vbox') > -1)) {
|
|
1640
|
+
mediaType = 'Virtual';
|
|
1641
|
+
}
|
|
1489
1642
|
if (size) {
|
|
1490
1643
|
let i = util.findObjectByKey(result, 'serialNum', serialNum);
|
|
1491
1644
|
if (i === -1 || serialNum === '') {
|
|
1492
1645
|
i = util.findObjectByKey(result, 'name', name);
|
|
1493
1646
|
}
|
|
1494
|
-
if (i
|
|
1647
|
+
if (i !== -1) {
|
|
1495
1648
|
result[i].type = mediaType;
|
|
1496
1649
|
result[i].interfaceType = interfaceType;
|
|
1497
1650
|
}
|
|
@@ -1506,16 +1659,16 @@ function diskLayout(callback) {
|
|
|
1506
1659
|
const smartData = JSON.parse(smartStr);
|
|
1507
1660
|
if (smartData.serial_number) {
|
|
1508
1661
|
const serialNum = smartData.serial_number;
|
|
1509
|
-
|
|
1510
|
-
if (i
|
|
1511
|
-
result[i].smartStatus =
|
|
1512
|
-
if (smartData.temperature
|
|
1662
|
+
const i = util.findObjectByKey(result, 'serialNum', serialNum);
|
|
1663
|
+
if (i !== -1) {
|
|
1664
|
+
result[i].smartStatus = smartData.smart_status?.passed ? 'Ok' : smartData.smart_status && smartData.smart_status.passed === false ? 'Predicted Failure' : 'unknown';
|
|
1665
|
+
if (smartData.temperature?.current) {
|
|
1513
1666
|
result[i].temperature = smartData.temperature.current;
|
|
1514
1667
|
}
|
|
1515
1668
|
result[i].smartData = smartData;
|
|
1516
1669
|
}
|
|
1517
1670
|
}
|
|
1518
|
-
} catch
|
|
1671
|
+
} catch {
|
|
1519
1672
|
util.noop();
|
|
1520
1673
|
}
|
|
1521
1674
|
});
|
|
@@ -1525,8 +1678,10 @@ function diskLayout(callback) {
|
|
|
1525
1678
|
}
|
|
1526
1679
|
resolve(result);
|
|
1527
1680
|
});
|
|
1528
|
-
} catch
|
|
1529
|
-
if (callback) {
|
|
1681
|
+
} catch {
|
|
1682
|
+
if (callback) {
|
|
1683
|
+
callback(result);
|
|
1684
|
+
}
|
|
1530
1685
|
resolve(result);
|
|
1531
1686
|
}
|
|
1532
1687
|
}
|