systeminformation 5.9.8 → 5.9.9

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 CHANGED
@@ -1,1265 +1,1265 @@
1
- 'use strict';
2
- // @ts-check
3
- // ==================================================================================
4
- // filesystem.js
5
- // ----------------------------------------------------------------------------------
6
- // Description: System Information - library
7
- // for Node.js
8
- // Copyright: (c) 2014 - 2021
9
- // Author: Sebastian Hildebrandt
10
- // ----------------------------------------------------------------------------------
11
- // License: MIT
12
- // ==================================================================================
13
- // 8. File System
14
- // ----------------------------------------------------------------------------------
15
-
16
- const util = require('./util');
17
- const fs = require('fs');
18
-
19
- const exec = require('child_process').exec;
20
- const execSync = require('child_process').execSync;
21
- const execPromiseSave = util.promisifySave(require('child_process').exec);
22
-
23
- let _platform = process.platform;
24
-
25
- const _linux = (_platform === 'linux');
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
-
33
- let _fs_speed = {};
34
- let _disk_io = {};
35
-
36
- // --------------------------
37
- // FS - mounted file systems
38
-
39
- function fsSize(callback) {
40
-
41
- let macOsDisks = [];
42
-
43
- function getmacOsFsType(fs) {
44
- if (!fs.startsWith('/')) { return 'NFS'; }
45
- const parts = fs.split('/');
46
- const fsShort = parts[parts.length - 1];
47
- const macOsDisksSingle = macOsDisks.filter(item => item.indexOf(fsShort) >= 0);
48
- if (macOsDisksSingle.length === 1 && macOsDisksSingle[0].indexOf('APFS') >= 0) { return 'APFS'; }
49
- return 'HFS';
50
- }
51
-
52
- function parseDf(lines) {
53
- let data = [];
54
- lines.forEach(function (line) {
55
- if (line !== '') {
56
- line = line.replace(/ +/g, ' ').split(' ');
57
- if (line && ((line[0].startsWith('/')) || (line[6] && line[6] === '/') || (line[0].indexOf('/') > 0) || (line[0].indexOf(':') === 1))) {
58
- const fs = line[0];
59
- const fsType = ((_linux || _freebsd || _openbsd || _netbsd) ? line[1] : getmacOsFsType(line[0]));
60
- const size = parseInt(((_linux || _freebsd || _openbsd || _netbsd) ? line[2] : line[1])) * 1024;
61
- const used = parseInt(((_linux || _freebsd || _openbsd || _netbsd) ? line[3] : line[2])) * 1024;
62
- const available = parseInt(((_linux || _freebsd || _openbsd || _netbsd) ? line[4] : line[3])) * 1024;
63
- const use = parseFloat((100.0 * (used / (used + available))).toFixed(2));
64
- line.splice(0, (_linux || _freebsd || _openbsd || _netbsd) ? 6 : 5);
65
- const mount = line.join(' ');
66
- // const mount = line[line.length - 1];
67
- if (!data.find(el => (el.fs === fs && el.type === fsType))) {
68
- data.push({
69
- fs,
70
- type: fsType,
71
- size,
72
- used,
73
- available,
74
- use,
75
- mount
76
- });
77
- }
78
- }
79
- }
80
- });
81
- return data;
82
- }
83
-
84
- return new Promise((resolve) => {
85
- process.nextTick(() => {
86
- let data = [];
87
- if (_linux || _freebsd || _openbsd || _netbsd || _darwin) {
88
- let cmd = '';
89
- if (_darwin) {
90
- cmd = 'df -kP';
91
- try {
92
- macOsDisks = execSync('diskutil list').toString().split('\n').filter(line => {
93
- return !line.startsWith('/') && line.indexOf(':') > 0;
94
- });
95
- } catch (e) {
96
- macOsDisks = [];
97
- }
98
- }
99
- if (_linux) { cmd = 'df -lkPTx squashfs | grep -E "^/|^.\\:"'; }
100
- if (_freebsd || _openbsd || _netbsd) { cmd = 'df -lkPT'; }
101
- exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) {
102
- if (!error) {
103
- let lines = stdout.toString().split('\n');
104
- data = parseDf(lines);
105
- if (callback) {
106
- callback(data);
107
- }
108
- resolve(data);
109
- } else {
110
- exec('df -kPT', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
111
- if (!error) {
112
- let lines = stdout.toString().split('\n');
113
- data = parseDf(lines);
114
- }
115
- if (callback) {
116
- callback(data);
117
- }
118
- resolve(data);
119
- });
120
- }
121
- });
122
- }
123
- if (_sunos) {
124
- if (callback) { callback(data); }
125
- resolve(data);
126
- }
127
- if (_windows) {
128
- try {
129
- util.wmic('logicaldisk get Caption,FileSystem,FreeSpace,Size').then((stdout) => {
130
- let lines = stdout.split('\r\n').filter(line => line.trim() !== '').filter((line, idx) => idx > 0);
131
- lines.forEach(function (line) {
132
- if (line !== '') {
133
- line = line.trim().split(/\s\s+/);
134
- if (line.length >= 4 && parseInt(line[3], 10)) {
135
- data.push({
136
- fs: line[0],
137
- type: line[1],
138
- size: parseInt(line[3], 10),
139
- used: parseInt(line[3], 10) - parseInt(line[2], 10),
140
- available: parseInt(line[2], 10),
141
- use: parseFloat(((100.0 * (parseInt(line[3]) - parseInt(line[2]))) / parseInt(line[3])).toFixed(2)),
142
- mount: line[0]
143
- });
144
- }
145
- }
146
- });
147
- if (callback) {
148
- callback(data);
149
- }
150
- resolve(data);
151
- });
152
- } catch (e) {
153
- if (callback) { callback(data); }
154
- resolve(data);
155
- }
156
- }
157
- });
158
- });
159
- }
160
-
161
- exports.fsSize = fsSize;
162
-
163
- // --------------------------
164
- // FS - open files count
165
-
166
- function fsOpenFiles(callback) {
167
-
168
- return new Promise((resolve) => {
169
- process.nextTick(() => {
170
- const result = {
171
- max: null,
172
- allocated: null,
173
- available: null
174
- };
175
- if (_freebsd || _openbsd || _netbsd || _darwin) {
176
- let cmd = 'sysctl -a | grep \'kern.*files\'';
177
- exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) {
178
- if (!error) {
179
- let lines = stdout.toString().split('\n');
180
- result.max = parseInt(util.getValue(lines, 'kern.maxfiles', ':'), 10);
181
- result.allocated = parseInt(util.getValue(lines, 'kern.num_files', ':'), 10);
182
- result.available = result.max - result.allocated;
183
- }
184
- if (callback) {
185
- callback(result);
186
- }
187
- resolve(result);
188
- });
189
- }
190
- if (_linux) {
191
- fs.readFile('/proc/sys/fs/file-nr', function (error, stdout) {
192
- if (!error) {
193
- let lines = stdout.toString().split('\n');
194
- if (lines[0]) {
195
- const parts = lines[0].replace(/\s+/g, ' ').split(' ');
196
- if (parts.length === 3) {
197
- result.allocated = parseInt(parts[0], 10);
198
- result.available = parseInt(parts[1], 10);
199
- result.max = parseInt(parts[2], 10);
200
- if (!result.available) { result.available = result.max - result.allocated; }
201
- }
202
- }
203
- if (callback) {
204
- callback(result);
205
- }
206
- resolve(result);
207
- } else {
208
- fs.readFile('/proc/sys/fs/file-max', function (error, stdout) {
209
- if (!error) {
210
- let lines = stdout.toString().split('\n');
211
- if (lines[0]) {
212
- result.max = parseInt(lines[0], 10);
213
- }
214
- }
215
- if (callback) {
216
- callback(result);
217
- }
218
- resolve(result);
219
- });
220
- }
221
- });
222
- }
223
- if (_sunos) {
224
- if (callback) { callback(null); }
225
- resolve(null);
226
- }
227
- if (_windows) {
228
- if (callback) { callback(null); }
229
- resolve(null);
230
- }
231
- });
232
- });
233
- }
234
-
235
- exports.fsOpenFiles = fsOpenFiles;
236
-
237
- // --------------------------
238
- // disks
239
-
240
- function parseBytes(s) {
241
- return parseInt(s.substr(s.indexOf(' (') + 2, s.indexOf(' Bytes)') - 10));
242
- }
243
-
244
- function parseDevices(lines) {
245
- let devices = [];
246
- let i = 0;
247
- lines.forEach(line => {
248
- if (line.length > 0) {
249
- if (line[0] === '*') {
250
- i++;
251
- } else {
252
- let parts = line.split(':');
253
- if (parts.length > 1) {
254
- if (!devices[i]) {
255
- devices[i] = {
256
- name: '',
257
- identifier: '',
258
- type: 'disk',
259
- fsType: '',
260
- mount: '',
261
- size: 0,
262
- physical: 'HDD',
263
- uuid: '',
264
- label: '',
265
- model: '',
266
- serial: '',
267
- removable: false,
268
- protocol: ''
269
- };
270
- }
271
- parts[0] = parts[0].trim().toUpperCase().replace(/ +/g, '');
272
- parts[1] = parts[1].trim();
273
- if ('DEVICEIDENTIFIER' === parts[0]) { devices[i].identifier = parts[1]; }
274
- if ('DEVICENODE' === parts[0]) { devices[i].name = parts[1]; }
275
- if ('VOLUMENAME' === parts[0]) {
276
- if (parts[1].indexOf('Not applicable') === -1) { devices[i].label = parts[1]; }
277
- }
278
- if ('PROTOCOL' === parts[0]) { devices[i].protocol = parts[1]; }
279
- if ('DISKSIZE' === parts[0]) { devices[i].size = parseBytes(parts[1]); }
280
- if ('FILESYSTEMPERSONALITY' === parts[0]) { devices[i].fsType = parts[1]; }
281
- if ('MOUNTPOINT' === parts[0]) { devices[i].mount = parts[1]; }
282
- if ('VOLUMEUUID' === parts[0]) { devices[i].uuid = parts[1]; }
283
- if ('READ-ONLYMEDIA' === parts[0] && parts[1] === 'Yes') { devices[i].physical = 'CD/DVD'; }
284
- if ('SOLIDSTATE' === parts[0] && parts[1] === 'Yes') { devices[i].physical = 'SSD'; }
285
- if ('VIRTUAL' === parts[0]) { devices[i].type = 'virtual'; }
286
- if ('REMOVABLEMEDIA' === parts[0]) { devices[i].removable = (parts[1] === 'Removable'); }
287
- if ('PARTITIONTYPE' === parts[0]) { devices[i].type = 'part'; }
288
- if ('DEVICE/MEDIANAME' === parts[0]) { devices[i].model = parts[1]; }
289
- }
290
- }
291
- }
292
- });
293
- return devices;
294
- }
295
-
296
- function parseBlk(lines) {
297
- let data = [];
298
-
299
- lines.filter(line => line !== '').forEach((line) => {
300
- try {
301
- line = decodeURIComponent(line.replace(/\\x/g, '%'));
302
- line = line.replace(/\\/g, '\\\\');
303
- let disk = JSON.parse(line);
304
- data.push({
305
- 'name': disk.name,
306
- 'type': disk.type,
307
- 'fsType': disk.fsType,
308
- 'mount': disk.mountpoint,
309
- 'size': parseInt(disk.size),
310
- 'physical': (disk.type === 'disk' ? (disk.rota === '0' ? 'SSD' : 'HDD') : (disk.type === 'rom' ? 'CD/DVD' : '')),
311
- 'uuid': disk.uuid,
312
- 'label': disk.label,
313
- 'model': disk.model,
314
- 'serial': disk.serial,
315
- 'removable': disk.rm === '1',
316
- 'protocol': disk.tran,
317
- 'group': disk.group,
318
- });
319
- } catch (e) {
320
- util.noop();
321
- }
322
- });
323
- data = util.unique(data);
324
- data = util.sortByKey(data, ['type', 'name']);
325
- return data;
326
- }
327
-
328
- function blkStdoutToObject(stdout) {
329
- return stdout.toString()
330
- .replace(/NAME=/g, '{"name":')
331
- .replace(/FSTYPE=/g, ',"fsType":')
332
- .replace(/TYPE=/g, ',"type":')
333
- .replace(/SIZE=/g, ',"size":')
334
- .replace(/MOUNTPOINT=/g, ',"mountpoint":')
335
- .replace(/UUID=/g, ',"uuid":')
336
- .replace(/ROTA=/g, ',"rota":')
337
- .replace(/RO=/g, ',"ro":')
338
- .replace(/RM=/g, ',"rm":')
339
- .replace(/TRAN=/g, ',"tran":')
340
- .replace(/SERIAL=/g, ',"serial":')
341
- .replace(/LABEL=/g, ',"label":')
342
- .replace(/MODEL=/g, ',"model":')
343
- .replace(/OWNER=/g, ',"owner":')
344
- .replace(/GROUP=/g, ',"group":')
345
- .replace(/\n/g, '}\n');
346
- }
347
-
348
- function blockDevices(callback) {
349
-
350
- return new Promise((resolve) => {
351
- process.nextTick(() => {
352
- let data = [];
353
- if (_linux) {
354
- // see https://wiki.ubuntuusers.de/lsblk/
355
- // 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) {
356
- exec('lsblk -bPo NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,UUID,ROTA,RO,RM,TRAN,SERIAL,LABEL,MODEL,OWNER 2>/dev/null', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
357
- if (!error) {
358
- let lines = blkStdoutToObject(stdout).split('\n');
359
- data = parseBlk(lines);
360
- if (callback) {
361
- callback(data);
362
- }
363
- resolve(data);
364
- } else {
365
- exec('lsblk -bPo NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,UUID,ROTA,RO,RM,LABEL,MODEL,OWNER 2>/dev/null', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
366
- if (!error) {
367
- let lines = blkStdoutToObject(stdout).split('\n');
368
- data = parseBlk(lines);
369
- }
370
- if (callback) {
371
- callback(data);
372
- }
373
- resolve(data);
374
- });
375
- }
376
- });
377
- }
378
- if (_darwin) {
379
- exec('diskutil info -all', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
380
- if (!error) {
381
- let lines = stdout.toString().split('\n');
382
- // parse lines into temp array of devices
383
- data = parseDevices(lines);
384
- }
385
- if (callback) {
386
- callback(data);
387
- }
388
- resolve(data);
389
- });
390
- }
391
- if (_sunos) {
392
- if (callback) { callback(data); }
393
- resolve(data);
394
- }
395
- if (_windows) {
396
- let drivetypes = ['Unknown', 'NoRoot', 'Removable', 'Local', 'Network', 'CD/DVD', 'RAM'];
397
- try {
398
- // util.wmic('logicaldisk get Caption,Description,DeviceID,DriveType,FileSystem,FreeSpace,Name,Size,VolumeName,VolumeSerialNumber /value').then((stdout, error) => {
399
- util.powerShell('Get-CimInstance -ClassName Win32_LogicalDisk | Format-List *').then((stdout, error) => {
400
- if (!error) {
401
- let devices = stdout.toString().split(/\n\s*\n/);
402
- devices.forEach(function (device) {
403
- let lines = device.split('\r\n');
404
- let drivetype = util.getValue(lines, 'drivetype', ':');
405
- if (drivetype) {
406
- data.push({
407
- name: util.getValue(lines, 'name', ':'),
408
- identifier: util.getValue(lines, 'caption', ':'),
409
- type: 'disk',
410
- fsType: util.getValue(lines, 'filesystem', ':').toLowerCase(),
411
- mount: util.getValue(lines, 'caption', ':'),
412
- size: util.getValue(lines, 'size', ':'),
413
- physical: (drivetype >= 0 && drivetype <= 6) ? drivetypes[drivetype] : drivetypes[0],
414
- uuid: util.getValue(lines, 'volumeserialnumber', ':'),
415
- label: util.getValue(lines, 'volumename', ':'),
416
- model: '',
417
- serial: util.getValue(lines, 'volumeserialnumber', ':'),
418
- removable: drivetype === '2',
419
- protocol: ''
420
- });
421
- }
422
- });
423
- }
424
- if (callback) {
425
- callback(data);
426
- }
427
- resolve(data);
428
- });
429
- } catch (e) {
430
- if (callback) { callback(data); }
431
- resolve(data);
432
- }
433
- }
434
- if (_freebsd || _openbsd || _netbsd) {
435
- // will follow
436
- if (callback) { callback(null); }
437
- resolve(null);
438
- }
439
-
440
- });
441
- });
442
- }
443
-
444
- exports.blockDevices = blockDevices;
445
-
446
- // --------------------------
447
- // FS - speed
448
-
449
- function calcFsSpeed(rx, wx) {
450
- let result = {
451
- rx: 0,
452
- wx: 0,
453
- tx: 0,
454
- rx_sec: null,
455
- wx_sec: null,
456
- tx_sec: null,
457
- ms: 0
458
- };
459
-
460
- if (_fs_speed && _fs_speed.ms) {
461
- result.rx = rx;
462
- result.wx = wx;
463
- result.tx = result.rx + result.wx;
464
- result.ms = Date.now() - _fs_speed.ms;
465
- result.rx_sec = (result.rx - _fs_speed.bytes_read) / (result.ms / 1000);
466
- result.wx_sec = (result.wx - _fs_speed.bytes_write) / (result.ms / 1000);
467
- result.tx_sec = result.rx_sec + result.wx_sec;
468
- _fs_speed.rx_sec = result.rx_sec;
469
- _fs_speed.wx_sec = result.wx_sec;
470
- _fs_speed.tx_sec = result.tx_sec;
471
- _fs_speed.bytes_read = result.rx;
472
- _fs_speed.bytes_write = result.wx;
473
- _fs_speed.bytes_overall = result.rx + result.wx;
474
- _fs_speed.ms = Date.now();
475
- _fs_speed.last_ms = result.ms;
476
- } else {
477
- result.rx = rx;
478
- result.wx = wx;
479
- result.tx = result.rx + result.wx;
480
- _fs_speed.rx_sec = null;
481
- _fs_speed.wx_sec = null;
482
- _fs_speed.tx_sec = null;
483
- _fs_speed.bytes_read = result.rx;
484
- _fs_speed.bytes_write = result.wx;
485
- _fs_speed.bytes_overall = result.rx + result.wx;
486
- _fs_speed.ms = Date.now();
487
- _fs_speed.last_ms = 0;
488
- }
489
- return result;
490
- }
491
-
492
- function fsStats(callback) {
493
-
494
- return new Promise((resolve) => {
495
- process.nextTick(() => {
496
- if (_windows) {
497
- return resolve(null);
498
- }
499
-
500
- let result = {
501
- rx: 0,
502
- wx: 0,
503
- tx: 0,
504
- rx_sec: null,
505
- wx_sec: null,
506
- tx_sec: null,
507
- ms: 0
508
- };
509
-
510
- let rx = 0;
511
- let wx = 0;
512
- if ((_fs_speed && !_fs_speed.ms) || (_fs_speed && _fs_speed.ms && Date.now() - _fs_speed.ms >= 500)) {
513
- if (_linux) {
514
- // exec("df -k | grep /dev/", function(error, stdout) {
515
- exec('lsblk -r 2>/dev/null | grep /', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
516
- if (!error) {
517
- let lines = stdout.toString().split('\n');
518
- let fs_filter = [];
519
- lines.forEach(function (line) {
520
- if (line !== '') {
521
- line = line.trim().split(' ');
522
- if (fs_filter.indexOf(line[0]) === -1) { fs_filter.push(line[0]); }
523
- }
524
- });
525
-
526
- let output = fs_filter.join('|');
527
- exec('cat /proc/diskstats | egrep "' + output + '"', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
528
- if (!error) {
529
- let lines = stdout.toString().split('\n');
530
- lines.forEach(function (line) {
531
- line = line.trim();
532
- if (line !== '') {
533
- line = line.replace(/ +/g, ' ').split(' ');
534
-
535
- rx += parseInt(line[5]) * 512;
536
- wx += parseInt(line[9]) * 512;
537
- }
538
- });
539
- result = calcFsSpeed(rx, wx);
540
- }
541
- if (callback) {
542
- callback(result);
543
- }
544
- resolve(result);
545
- });
546
- } else {
547
- if (callback) {
548
- callback(result);
549
- }
550
- resolve(result);
551
- }
552
- });
553
- }
554
- if (_darwin) {
555
- exec('ioreg -c IOBlockStorageDriver -k Statistics -r -w0 | sed -n "/IOBlockStorageDriver/,/Statistics/p" | grep "Statistics" | tr -cd "01234567890,\n"', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
556
- if (!error) {
557
- let lines = stdout.toString().split('\n');
558
- lines.forEach(function (line) {
559
- line = line.trim();
560
- if (line !== '') {
561
- line = line.split(',');
562
-
563
- rx += parseInt(line[2]);
564
- wx += parseInt(line[9]);
565
- }
566
- });
567
- result = calcFsSpeed(rx, wx);
568
- }
569
- if (callback) {
570
- callback(result);
571
- }
572
- resolve(result);
573
- });
574
- }
575
- } else {
576
- result.ms = _fs_speed.last_ms;
577
- result.rx = _fs_speed.bytes_read;
578
- result.wx = _fs_speed.bytes_write;
579
- result.tx = _fs_speed.bytes_read + _fs_speed.bytes_write;
580
- result.rx_sec = _fs_speed.rx_sec;
581
- result.wx_sec = _fs_speed.wx_sec;
582
- result.tx_sec = _fs_speed.tx_sec;
583
- if (callback) {
584
- callback(result);
585
- }
586
- resolve(result);
587
- }
588
- });
589
- });
590
- }
591
-
592
- exports.fsStats = fsStats;
593
-
594
- function calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime) {
595
- let result = {
596
- rIO: 0,
597
- wIO: 0,
598
- tIO: 0,
599
- rIO_sec: null,
600
- wIO_sec: null,
601
- tIO_sec: null,
602
- rWaitTime: 0,
603
- wWaitTime: 0,
604
- tWaitTime: 0,
605
- rWaitPercent: null,
606
- wWaitPercent: null,
607
- tWaitPercent: null,
608
- ms: 0
609
- };
610
- if (_disk_io && _disk_io.ms) {
611
- result.rIO = rIO;
612
- result.wIO = wIO;
613
- result.tIO = rIO + wIO;
614
- result.ms = Date.now() - _disk_io.ms;
615
- result.rIO_sec = (result.rIO - _disk_io.rIO) / (result.ms / 1000);
616
- result.wIO_sec = (result.wIO - _disk_io.wIO) / (result.ms / 1000);
617
- result.tIO_sec = result.rIO_sec + result.wIO_sec;
618
- result.rWaitTime = rWaitTime;
619
- result.wWaitTime = wWaitTime;
620
- result.tWaitTime = tWaitTime;
621
- result.rWaitPercent = (result.rWaitTime - _disk_io.rWaitTime) * 100 / (result.ms);
622
- result.wWaitPercent = (result.wWaitTime - _disk_io.wWaitTime) * 100 / (result.ms);
623
- result.tWaitPercent = (result.tWaitTime - _disk_io.tWaitTime) * 100 / (result.ms);
624
- _disk_io.rIO = rIO;
625
- _disk_io.wIO = wIO;
626
- _disk_io.rIO_sec = result.rIO_sec;
627
- _disk_io.wIO_sec = result.wIO_sec;
628
- _disk_io.tIO_sec = result.tIO_sec;
629
- _disk_io.rWaitTime = rWaitTime;
630
- _disk_io.wWaitTime = wWaitTime;
631
- _disk_io.tWaitTime = tWaitTime;
632
- _disk_io.rWaitPercent = result.rWaitPercent;
633
- _disk_io.wWaitPercent = result.wWaitPercent;
634
- _disk_io.tWaitPercent = result.tWaitPercent;
635
- _disk_io.last_ms = result.ms;
636
- _disk_io.ms = Date.now();
637
- } else {
638
- result.rIO = rIO;
639
- result.wIO = wIO;
640
- result.tIO = rIO + wIO;
641
- result.rWaitTime = rWaitTime;
642
- result.wWaitTime = wWaitTime;
643
- result.tWaitTime = tWaitTime;
644
- _disk_io.rIO = rIO;
645
- _disk_io.wIO = wIO;
646
- _disk_io.rIO_sec = null;
647
- _disk_io.wIO_sec = null;
648
- _disk_io.tIO_sec = null;
649
- _disk_io.rWaitTime = rWaitTime;
650
- _disk_io.wWaitTime = wWaitTime;
651
- _disk_io.tWaitTime = tWaitTime;
652
- _disk_io.rWaitPercent = null;
653
- _disk_io.wWaitPercent = null;
654
- _disk_io.tWaitPercent = null;
655
- _disk_io.last_ms = 0;
656
- _disk_io.ms = Date.now();
657
- }
658
- return result;
659
- }
660
-
661
- function disksIO(callback) {
662
-
663
- return new Promise((resolve) => {
664
- process.nextTick(() => {
665
- if (_windows) {
666
- return resolve(null);
667
- }
668
- if (_sunos) {
669
- return resolve(null);
670
- }
671
-
672
- let result = {
673
- rIO: 0,
674
- wIO: 0,
675
- tIO: 0,
676
- rIO_sec: null,
677
- wIO_sec: null,
678
- tIO_sec: null,
679
- rWaitTime: 0,
680
- wWaitTime: 0,
681
- tWaitTime: 0,
682
- rWaitPercent: null,
683
- wWaitPercent: null,
684
- tWaitPercent: null,
685
- ms: 0
686
- };
687
- let rIO = 0;
688
- let wIO = 0;
689
- let rWaitTime = 0;
690
- let wWaitTime = 0;
691
- let tWaitTime = 0;
692
-
693
- if ((_disk_io && !_disk_io.ms) || (_disk_io && _disk_io.ms && Date.now() - _disk_io.ms >= 500)) {
694
- if (_linux || _freebsd || _openbsd || _netbsd) {
695
- // prints Block layer statistics for all mounted volumes
696
- // 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";
697
- // 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";
698
- let cmd = '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';
699
-
700
- exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) {
701
- if (!error) {
702
- let lines = stdout.split('\n');
703
- lines.forEach(function (line) {
704
- // ignore empty lines
705
- if (!line) { return; }
706
-
707
- // sum r/wIO of all disks to compute all disks IO
708
- let stats = line.split(';');
709
- rIO += parseInt(stats[0]);
710
- wIO += parseInt(stats[4]);
711
- rWaitTime += parseInt(stats[3]);
712
- wWaitTime += parseInt(stats[7]);
713
- tWaitTime += parseInt(stats[10]);
714
- });
715
- result = calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime);
716
-
717
- if (callback) {
718
- callback(result);
719
- }
720
- resolve(result);
721
- } else {
722
- if (callback) {
723
- callback(result);
724
- }
725
- resolve(result);
726
- }
727
- });
728
- }
729
- if (_darwin) {
730
- exec('ioreg -c IOBlockStorageDriver -k Statistics -r -w0 | sed -n "/IOBlockStorageDriver/,/Statistics/p" | grep "Statistics" | tr -cd "01234567890,\n"', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
731
- if (!error) {
732
- let lines = stdout.toString().split('\n');
733
- lines.forEach(function (line) {
734
- line = line.trim();
735
- if (line !== '') {
736
- line = line.split(',');
737
-
738
- rIO += parseInt(line[10]);
739
- wIO += parseInt(line[0]);
740
- }
741
- });
742
- result = calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime);
743
- }
744
- if (callback) {
745
- callback(result);
746
- }
747
- resolve(result);
748
- });
749
- }
750
- } else {
751
- result.rIO = _disk_io.rIO;
752
- result.wIO = _disk_io.wIO;
753
- result.tIO = _disk_io.rIO + _disk_io.wIO;
754
- result.ms = _disk_io.last_ms;
755
- result.rIO_sec = _disk_io.rIO_sec;
756
- result.wIO_sec = _disk_io.wIO_sec;
757
- result.tIO_sec = _disk_io.tIO_sec;
758
- result.rWaitTime = _disk_io.rWaitTime;
759
- result.wWaitTime = _disk_io.wWaitTime;
760
- result.tWaitTime = _disk_io.tWaitTime;
761
- result.rWaitPercent = _disk_io.rWaitPercent;
762
- result.wWaitPercent = _disk_io.wWaitPercent;
763
- result.tWaitPercent = _disk_io.tWaitPercent;
764
- if (callback) {
765
- callback(result);
766
- }
767
- resolve(result);
768
- }
769
- });
770
- });
771
- }
772
-
773
- exports.disksIO = disksIO;
774
-
775
- function diskLayout(callback) {
776
-
777
- function getVendorFromModel(model) {
778
- const diskManufacturers = [
779
- { pattern: '^WESTERN.+', manufacturer: 'Western Digital' },
780
- { pattern: '^WDC.+', manufacturer: 'Western Digital' },
781
- { pattern: 'WD.+', manufacturer: 'Western Digital' },
782
- { pattern: '^TOSHIBA.+', manufacturer: 'Toshiba' },
783
- { pattern: '^HITACHI.+', manufacturer: 'Hitachi' },
784
- { pattern: '^IC.+', manufacturer: 'Hitachi' },
785
- { pattern: '^HTS.+', manufacturer: 'Hitachi' },
786
- { pattern: '^SANDISK.+', manufacturer: 'SanDisk' },
787
- { pattern: '^KINGSTON.+', manufacturer: 'Kingston Technonogy' },
788
- { pattern: '^SONY.+', manufacturer: 'Sony' },
789
- { pattern: '^TRANSCEND.+', manufacturer: 'Transcend' },
790
- { pattern: 'SAMSUNG.+', manufacturer: 'Samsung' },
791
- { pattern: '^ST(?!I\\ ).+', manufacturer: 'Seagate' },
792
- { pattern: '^STI\\ .+', manufacturer: 'SimpleTech' },
793
- { pattern: '^D...-.+', manufacturer: 'IBM' },
794
- { pattern: '^IBM.+', manufacturer: 'IBM' },
795
- { pattern: '^FUJITSU.+', manufacturer: 'Fujitsu' },
796
- { pattern: '^MP.+', manufacturer: 'Fujitsu' },
797
- { pattern: '^MK.+', manufacturer: 'Toshiba' },
798
- { pattern: '^MAXTOR.+', manufacturer: 'Maxtor' },
799
- { pattern: '^Pioneer.+', manufacturer: 'Pioneer' },
800
- { pattern: '^PHILIPS.+', manufacturer: 'Philips' },
801
- { pattern: '^QUANTUM.+', manufacturer: 'Quantum Technology' },
802
- { pattern: 'FIREBALL.+', manufacturer: 'Quantum Technology' },
803
- { pattern: '^VBOX.+', manufacturer: 'VirtualBox' },
804
- { pattern: 'CORSAIR.+', manufacturer: 'Corsair Components' },
805
- { pattern: 'CRUCIAL.+', manufacturer: 'Crucial' },
806
- { pattern: 'ECM.+', manufacturer: 'ECM' },
807
- { pattern: 'INTEL.+', manufacturer: 'INTEL' },
808
- { pattern: '.+EVO', manufacturer: 'Samsung' },
809
- { pattern: 'APPLE.+', manufacturer: 'Apple' },
810
- ];
811
-
812
- let result = '';
813
- if (model) {
814
- model = model.toUpperCase();
815
- diskManufacturers.forEach((manufacturer) => {
816
- const re = RegExp(manufacturer.pattern);
817
- if (re.test(model)) { result = manufacturer.manufacturer; }
818
- });
819
- }
820
- return result;
821
- }
822
-
823
- return new Promise((resolve) => {
824
- process.nextTick(() => {
825
-
826
- const commitResult = res => {
827
- for (let i = 0; i < res.length; i++) {
828
- delete res[i].BSDName;
829
- }
830
- if (callback) {
831
- callback(res);
832
- }
833
- resolve(res);
834
- };
835
-
836
- let result = [];
837
- let cmd = '';
838
-
839
- if (_linux) {
840
- let cmdFullSmart = '';
841
-
842
- exec('export LC_ALL=C; lsblk -ablJO 2>/dev/null; unset LC_ALL', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
843
- if (!error) {
844
- try {
845
- const out = stdout.toString().trim();
846
- let devices = [];
847
- try {
848
- const outJSON = JSON.parse(out);
849
- if (outJSON && {}.hasOwnProperty.call(outJSON, 'blockdevices')) {
850
- devices = outJSON.blockdevices.filter(item => { return (item.type === 'disk') && item.size > 0 && (item.model !== null || (item.mountpoint === null && item.label === null && item.fsType === null && item.parttype === null)); });
851
- }
852
- } catch (e) {
853
- // fallback to older version of lsblk
854
- const out2 = execSync('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').toString();
855
- let lines = blkStdoutToObject(out2).split('\n');
856
- const data = parseBlk(lines);
857
- devices = data.filter(item => { return (item.type === 'disk') && item.size > 0 && ((item.model !== null && item.model !== '') || (item.mount === '' && item.label === '' && item.fsType === '')); });
858
- }
859
- devices.forEach((device) => {
860
- let mediumType = '';
861
- const BSDName = '/dev/' + device.name;
862
- const logical = device.name;
863
- try {
864
- mediumType = execSync('cat /sys/block/' + logical + '/queue/rotational 2>/dev/null').toString().split('\n')[0];
865
- } catch (e) {
866
- util.noop();
867
- }
868
- let interfaceType = device.tran ? device.tran.toUpperCase().trim() : '';
869
- if (interfaceType === 'NVME') {
870
- mediumType = '2';
871
- interfaceType = 'PCIe';
872
- }
873
- result.push({
874
- device: BSDName,
875
- type: (mediumType === '0' ? 'SSD' : (mediumType === '1' ? 'HD' : (mediumType === '2' ? 'NVMe' : (device.model && device.model.indexOf('SSD') > -1 ? 'SSD' : (device.model && device.model.indexOf('NVM') > -1 ? 'NVMe' : 'HD'))))),
876
- name: device.model || '',
877
- vendor: getVendorFromModel(device.model) || (device.vendor ? device.vendor.trim() : ''),
878
- size: device.size || 0,
879
- bytesPerSector: null,
880
- totalCylinders: null,
881
- totalHeads: null,
882
- totalSectors: null,
883
- totalTracks: null,
884
- tracksPerCylinder: null,
885
- sectorsPerTrack: null,
886
- firmwareRevision: device.rev ? device.rev.trim() : '',
887
- serialNum: device.serial ? device.serial.trim() : '',
888
- interfaceType: interfaceType,
889
- smartStatus: 'unknown',
890
- temperature: null,
891
- BSDName: BSDName
892
- });
893
- cmd += `printf "\n${BSDName}|"; smartctl -H ${BSDName} | grep overall;`;
894
- cmdFullSmart += `${cmdFullSmart ? 'printf ",";' : ''}smartctl -a -j ${BSDName};`;
895
- });
896
- } catch (e) {
897
- util.noop();
898
- }
899
- }
900
- // check S.M.A.R.T. status
901
- if (cmdFullSmart) {
902
- exec(cmdFullSmart, { maxBuffer: 1024 * 1024 }, function (error, stdout) {
903
- try {
904
- const data = JSON.parse(`[${stdout}]`);
905
- data.forEach(disk => {
906
- const diskBSDName = disk.smartctl.argv[disk.smartctl.argv.length - 1];
907
-
908
- for (let i = 0; i < result.length; i++) {
909
- if (result[i].BSDName === diskBSDName) {
910
- result[i].smartStatus = (disk.smart_status.passed ? 'Ok' : (disk.smart_status.passed === false ? 'Predicted Failure' : 'unknown'));
911
- if (disk.temperature && disk.temperature.current) {
912
- result[i].temperature = disk.temperature.current;
913
- }
914
- result[i].smartData = disk;
915
- }
916
- }
917
- });
918
- commitResult(result);
919
- } catch (e) {
920
- if (cmd) {
921
- cmd = cmd + 'printf "\n"';
922
- exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) {
923
- let lines = stdout.toString().split('\n');
924
- lines.forEach(line => {
925
- if (line) {
926
- let parts = line.split('|');
927
- if (parts.length === 2) {
928
- let BSDName = parts[0];
929
- parts[1] = parts[1].trim();
930
- let parts2 = parts[1].split(':');
931
- if (parts2.length === 2) {
932
- parts2[1] = parts2[1].trim();
933
- let status = parts2[1].toLowerCase();
934
- for (let i = 0; i < result.length; i++) {
935
- if (result[i].BSDName === BSDName) {
936
- result[i].smartStatus = (status === 'passed' ? 'Ok' : (status === 'failed!' ? 'Predicted Failure' : 'unknown'));
937
- }
938
- }
939
- }
940
- }
941
- }
942
- });
943
- commitResult(result);
944
- });
945
- } else {
946
- commitResult(result);
947
- }
948
- }
949
- });
950
- } else {
951
- commitResult(result);
952
- }
953
- });
954
- }
955
- if (_freebsd || _openbsd || _netbsd) {
956
- if (callback) { callback(result); }
957
- resolve(result);
958
- }
959
- if (_sunos) {
960
- if (callback) { callback(result); }
961
- resolve(result);
962
- }
963
- if (_darwin) {
964
- exec('system_profiler SPSerialATADataType SPNVMeDataType SPUSBDataType', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
965
- if (!error) {
966
- // split by type:
967
- let lines = stdout.toString().split('\n');
968
- let linesSATA = [];
969
- let linesNVMe = [];
970
- let linesUSB = [];
971
- let dataType = 'SATA';
972
- lines.forEach(line => {
973
- if (line === 'NVMExpress:') { dataType = 'NVMe'; }
974
- else if (line === 'USB:') { dataType = 'USB'; }
975
- else if (line === 'SATA/SATA Express:') { dataType = 'SATA'; }
976
- else if (dataType === 'SATA') { linesSATA.push(line); }
977
- else if (dataType === 'NVMe') { linesNVMe.push(line); }
978
- else if (dataType === 'USB') { linesUSB.push(line); }
979
- });
980
- try {
981
- // Serial ATA Drives
982
- let devices = linesSATA.join('\n').split(' Physical Interconnect: ');
983
- devices.shift();
984
- devices.forEach(function (device) {
985
- device = 'InterfaceType: ' + device;
986
- let lines = device.split('\n');
987
- const mediumType = util.getValue(lines, 'Medium Type', ':', true).trim();
988
- const sizeStr = util.getValue(lines, 'capacity', ':', true).trim();
989
- const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim();
990
- if (sizeStr) {
991
- let sizeValue = 0;
992
- if (sizeStr.indexOf('(') >= 0) {
993
- sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, '').replace(/\s/g, ''));
994
- }
995
- if (!sizeValue) {
996
- sizeValue = parseInt(sizeStr);
997
- }
998
- if (sizeValue) {
999
- const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase();
1000
- result.push({
1001
- device: BSDName,
1002
- type: mediumType.startsWith('Solid') ? 'SSD' : 'HD',
1003
- name: util.getValue(lines, 'Model', ':', true).trim(),
1004
- vendor: getVendorFromModel(util.getValue(lines, 'Model', ':', true).trim()) || util.getValue(lines, 'Manufacturer', ':', true),
1005
- size: sizeValue,
1006
- bytesPerSector: null,
1007
- totalCylinders: null,
1008
- totalHeads: null,
1009
- totalSectors: null,
1010
- totalTracks: null,
1011
- tracksPerCylinder: null,
1012
- sectorsPerTrack: null,
1013
- firmwareRevision: util.getValue(lines, 'Revision', ':', true).trim(),
1014
- serialNum: util.getValue(lines, 'Serial Number', ':', true).trim(),
1015
- interfaceType: util.getValue(lines, 'InterfaceType', ':', true).trim(),
1016
- smartStatus: smartStatusString === 'verified' ? 'OK' : smartStatusString || 'unknown',
1017
- temperature: null,
1018
- BSDName: BSDName
1019
- });
1020
- cmd = cmd + 'printf "\n' + BSDName + '|"; diskutil info /dev/' + BSDName + ' | grep SMART;';
1021
- }
1022
- }
1023
- });
1024
- } catch (e) {
1025
- util.noop();
1026
- }
1027
-
1028
- // NVME Drives
1029
- try {
1030
- let devices = linesNVMe.join('\n').split('\n\n Capacity:');
1031
- devices.shift();
1032
- devices.forEach(function (device) {
1033
- device = '!Capacity: ' + device;
1034
- let lines = device.split('\n');
1035
- const linkWidth = util.getValue(lines, 'link width', ':', true).trim();
1036
- const sizeStr = util.getValue(lines, '!capacity', ':', true).trim();
1037
- const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim();
1038
- if (sizeStr) {
1039
- let sizeValue = 0;
1040
- if (sizeStr.indexOf('(') >= 0) {
1041
- sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, '').replace(/\s/g, ''));
1042
- }
1043
- if (!sizeValue) {
1044
- sizeValue = parseInt(sizeStr);
1045
- }
1046
- if (sizeValue) {
1047
- const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase();
1048
- result.push({
1049
- device: BSDName,
1050
- type: 'NVMe',
1051
- name: util.getValue(lines, 'Model', ':', true).trim(),
1052
- vendor: getVendorFromModel(util.getValue(lines, 'Model', ':', true).trim()),
1053
- size: sizeValue,
1054
- bytesPerSector: null,
1055
- totalCylinders: null,
1056
- totalHeads: null,
1057
- totalSectors: null,
1058
- totalTracks: null,
1059
- tracksPerCylinder: null,
1060
- sectorsPerTrack: null,
1061
- firmwareRevision: util.getValue(lines, 'Revision', ':', true).trim(),
1062
- serialNum: util.getValue(lines, 'Serial Number', ':', true).trim(),
1063
- interfaceType: ('PCIe ' + linkWidth).trim(),
1064
- smartStatus: smartStatusString === 'verified' ? 'OK' : smartStatusString || 'unknown',
1065
- temperature: null,
1066
- BSDName: BSDName
1067
- });
1068
- cmd = cmd + 'printf "\n' + BSDName + '|"; diskutil info /dev/' + BSDName + ' | grep SMART;';
1069
- }
1070
- }
1071
- });
1072
- } catch (e) {
1073
- util.noop();
1074
- }
1075
- // USB Drives
1076
- try {
1077
- let devices = linesUSB.join('\n').replaceAll('Media:\n ', 'Model:').split('\n\n Product ID:');
1078
- devices.shift();
1079
- devices.forEach(function (device) {
1080
- let lines = device.split('\n');
1081
- const sizeStr = util.getValue(lines, 'Capacity', ':', true).trim();
1082
- const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim();
1083
- if (sizeStr) {
1084
- let sizeValue = 0;
1085
- if (sizeStr.indexOf('(') >= 0) {
1086
- sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, '').replace(/\s/g, ''));
1087
- }
1088
- if (!sizeValue) {
1089
- sizeValue = parseInt(sizeStr);
1090
- }
1091
- if (sizeValue) {
1092
- const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase();
1093
- result.push({
1094
- device: BSDName,
1095
- type: 'USB',
1096
- name: util.getValue(lines, 'Model', ':', true).trim().replaceAll(':', ''),
1097
- vendor: getVendorFromModel(util.getValue(lines, 'Model', ':', true).trim()),
1098
- size: sizeValue,
1099
- bytesPerSector: null,
1100
- totalCylinders: null,
1101
- totalHeads: null,
1102
- totalSectors: null,
1103
- totalTracks: null,
1104
- tracksPerCylinder: null,
1105
- sectorsPerTrack: null,
1106
- firmwareRevision: util.getValue(lines, 'Revision', ':', true).trim(),
1107
- serialNum: util.getValue(lines, 'Serial Number', ':', true).trim(),
1108
- interfaceType: 'USB',
1109
- smartStatus: smartStatusString === 'verified' ? 'OK' : smartStatusString || 'unknown',
1110
- temperature: null,
1111
- BSDName: BSDName
1112
- });
1113
- cmd = cmd + 'printf "\n' + BSDName + '|"; diskutil info /dev/' + BSDName + ' | grep SMART;';
1114
- }
1115
- }
1116
- });
1117
- } catch (e) {
1118
- util.noop();
1119
- }
1120
- if (cmd) {
1121
- cmd = cmd + 'printf "\n"';
1122
- exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) {
1123
- let lines = stdout.toString().split('\n');
1124
- lines.forEach(line => {
1125
- if (line) {
1126
- let parts = line.split('|');
1127
- if (parts.length === 2) {
1128
- let BSDName = parts[0];
1129
- parts[1] = parts[1].trim();
1130
- let parts2 = parts[1].split(':');
1131
- if (parts2.length === 2) {
1132
- parts2[1] = parts2[1].trim();
1133
- let status = parts2[1].toLowerCase();
1134
- for (let i = 0; i < result.length; i++) {
1135
- if (result[i].BSDName === BSDName) {
1136
- result[i].smartStatus = (status === 'not supported' ? 'not supported' : (status === 'verified' ? 'Ok' : (status === 'failing' ? 'Predicted Failure' : 'unknown')));
1137
- }
1138
- }
1139
- }
1140
- }
1141
- }
1142
- });
1143
- for (let i = 0; i < result.length; i++) {
1144
- delete result[i].BSDName;
1145
- }
1146
- if (callback) {
1147
- callback(result);
1148
- }
1149
- resolve(result);
1150
- });
1151
- } else {
1152
- for (let i = 0; i < result.length; i++) {
1153
- delete result[i].BSDName;
1154
- }
1155
- if (callback) {
1156
- callback(result);
1157
- }
1158
- resolve(result);
1159
- }
1160
- }
1161
- });
1162
- }
1163
- if (_windows) {
1164
- try {
1165
- const workload = [];
1166
- workload.push(util.powerShell('Get-WmiObject Win32_DiskDrive | fl *'));
1167
- workload.push(util.powerShell('Get-PhysicalDisk | Format-List'));
1168
- if (util.smartMonToolsInstalled()) {
1169
- try {
1170
- const smartDev = JSON.parse(execSync('smartctl --scan -j'));
1171
- if (smartDev && smartDev.devices && smartDev.devices.length > 0) {
1172
- smartDev.devices.forEach((dev) => {
1173
- workload.push(execPromiseSave(`smartctl -j -a ${dev.name}`, util.execOptsWin));
1174
- });
1175
- }
1176
- } catch (e) {
1177
- util.noop();
1178
- }
1179
- }
1180
- util.promiseAll(
1181
- workload
1182
- ).then(data => {
1183
- let devices = data.results[0].toString().split(/\n\s*\n/);
1184
- devices.forEach(function (device) {
1185
- let lines = device.split('\r\n');
1186
- const size = util.getValue(lines, 'Size', ':').trim();
1187
- const status = util.getValue(lines, 'Status', ':').trim().toLowerCase();
1188
- if (size) {
1189
- result.push({
1190
- device: util.getValue(lines, 'PNPDeviceId', ':'),
1191
- type: device.indexOf('SSD') > -1 ? 'SSD' : 'HD', // just a starting point ... better: MSFT_PhysicalDisk - Media Type ... see below
1192
- name: util.getValue(lines, 'Caption', ':'),
1193
- vendor: getVendorFromModel(util.getValue(lines, 'Caption', ':', true).trim()),
1194
- size: parseInt(size),
1195
- bytesPerSector: parseInt(util.getValue(lines, 'BytesPerSector', ':')),
1196
- totalCylinders: parseInt(util.getValue(lines, 'TotalCylinders', ':')),
1197
- totalHeads: parseInt(util.getValue(lines, 'TotalHeads', ':')),
1198
- totalSectors: parseInt(util.getValue(lines, 'TotalSectors', ':')),
1199
- totalTracks: parseInt(util.getValue(lines, 'TotalTracks', ':')),
1200
- tracksPerCylinder: parseInt(util.getValue(lines, 'TracksPerCylinder', ':')),
1201
- sectorsPerTrack: parseInt(util.getValue(lines, 'SectorsPerTrack', ':')),
1202
- firmwareRevision: util.getValue(lines, 'FirmwareRevision', ':').trim(),
1203
- serialNum: util.getValue(lines, 'SerialNumber', ':').trim(),
1204
- interfaceType: util.getValue(lines, 'InterfaceType', ':').trim(),
1205
- smartStatus: (status === 'ok' ? 'Ok' : (status === 'degraded' ? 'Degraded' : (status === 'pred fail' ? 'Predicted Failure' : 'Unknown'))),
1206
- temperature: null,
1207
- });
1208
- }
1209
- });
1210
- devices = data.results[1].split(/\n\s*\n/);
1211
- devices.forEach(function (device) {
1212
- let lines = device.split('\r\n');
1213
- const serialNum = util.getValue(lines, 'SerialNumber', ':').trim();
1214
- const name = util.getValue(lines, 'FriendlyName', ':').trim().replace('Msft ', 'Microsoft');
1215
- const size = util.getValue(lines, 'Size', ':').trim();
1216
- const model = util.getValue(lines, 'Model', ':').trim();
1217
- const interfaceType = util.getValue(lines, 'BusType', ':').trim();
1218
- let mediaType = util.getValue(lines, 'MediaType', ':').trim();
1219
- if (mediaType === '3' || mediaType === 'HDD') { mediaType = 'HD'; }
1220
- if (mediaType === '4') { mediaType = 'SSD'; }
1221
- if (mediaType === '5') { mediaType = 'SCM'; }
1222
- if (mediaType === 'Unspecified' && (model.toLowerCase().indexOf('virtual') > -1 || model.toLowerCase().indexOf('vbox') > -1)) { mediaType = 'Virtual'; }
1223
- if (size) {
1224
- let i = util.findObjectByKey(result, 'serialNum', serialNum);
1225
- if (i === -1 || serialNum === '') {
1226
- i = util.findObjectByKey(result, 'name', name);
1227
- }
1228
- if (i != -1) {
1229
- result[i].type = mediaType;
1230
- result[i].interfaceType = interfaceType;
1231
- }
1232
- }
1233
- });
1234
- // S.M.A.R.T
1235
- data.results.shift();
1236
- data.results.shift();
1237
- data.results.forEach((smartStr) => {
1238
- const smartData = JSON.parse(smartStr);
1239
- if (smartData.serial_number) {
1240
- const serialNum = smartData.serial_number;
1241
- let i = util.findObjectByKey(result, 'serialNum', serialNum);
1242
- if (i != -1) {
1243
- result[i].smartStatus = (smartData.smart_status.passed ? 'Ok' : (smartData.smart_status.passed === false ? 'Predicted Failure' : 'unknown'));
1244
- if (smartData.temperature && smartData.temperature.current) {
1245
- result[i].temperature = smartData.temperature.current;
1246
- }
1247
- result[i].smartData = smartData;
1248
- }
1249
- }
1250
- });
1251
- if (callback) {
1252
- callback(result);
1253
- }
1254
- resolve(result);
1255
- });
1256
- } catch (e) {
1257
- if (callback) { callback(result); }
1258
- resolve(result);
1259
- }
1260
- }
1261
- });
1262
- });
1263
- }
1264
-
1265
- exports.diskLayout = diskLayout;
1
+ 'use strict';
2
+ // @ts-check
3
+ // ==================================================================================
4
+ // filesystem.js
5
+ // ----------------------------------------------------------------------------------
6
+ // Description: System Information - library
7
+ // for Node.js
8
+ // Copyright: (c) 2014 - 2021
9
+ // Author: Sebastian Hildebrandt
10
+ // ----------------------------------------------------------------------------------
11
+ // License: MIT
12
+ // ==================================================================================
13
+ // 8. File System
14
+ // ----------------------------------------------------------------------------------
15
+
16
+ const util = require('./util');
17
+ const fs = require('fs');
18
+
19
+ const exec = require('child_process').exec;
20
+ const execSync = require('child_process').execSync;
21
+ const execPromiseSave = util.promisifySave(require('child_process').exec);
22
+
23
+ let _platform = process.platform;
24
+
25
+ const _linux = (_platform === 'linux');
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
+
33
+ let _fs_speed = {};
34
+ let _disk_io = {};
35
+
36
+ // --------------------------
37
+ // FS - mounted file systems
38
+
39
+ function fsSize(callback) {
40
+
41
+ let macOsDisks = [];
42
+
43
+ function getmacOsFsType(fs) {
44
+ if (!fs.startsWith('/')) { return 'NFS'; }
45
+ const parts = fs.split('/');
46
+ const fsShort = parts[parts.length - 1];
47
+ const macOsDisksSingle = macOsDisks.filter(item => item.indexOf(fsShort) >= 0);
48
+ if (macOsDisksSingle.length === 1 && macOsDisksSingle[0].indexOf('APFS') >= 0) { return 'APFS'; }
49
+ return 'HFS';
50
+ }
51
+
52
+ function parseDf(lines) {
53
+ let data = [];
54
+ lines.forEach(function (line) {
55
+ if (line !== '') {
56
+ line = line.replace(/ +/g, ' ').split(' ');
57
+ if (line && ((line[0].startsWith('/')) || (line[6] && line[6] === '/') || (line[0].indexOf('/') > 0) || (line[0].indexOf(':') === 1))) {
58
+ const fs = line[0];
59
+ const fsType = ((_linux || _freebsd || _openbsd || _netbsd) ? line[1] : getmacOsFsType(line[0]));
60
+ const size = parseInt(((_linux || _freebsd || _openbsd || _netbsd) ? line[2] : line[1])) * 1024;
61
+ const used = parseInt(((_linux || _freebsd || _openbsd || _netbsd) ? line[3] : line[2])) * 1024;
62
+ const available = parseInt(((_linux || _freebsd || _openbsd || _netbsd) ? line[4] : line[3])) * 1024;
63
+ const use = parseFloat((100.0 * (used / (used + available))).toFixed(2));
64
+ line.splice(0, (_linux || _freebsd || _openbsd || _netbsd) ? 6 : 5);
65
+ const mount = line.join(' ');
66
+ // const mount = line[line.length - 1];
67
+ if (!data.find(el => (el.fs === fs && el.type === fsType))) {
68
+ data.push({
69
+ fs,
70
+ type: fsType,
71
+ size,
72
+ used,
73
+ available,
74
+ use,
75
+ mount
76
+ });
77
+ }
78
+ }
79
+ }
80
+ });
81
+ return data;
82
+ }
83
+
84
+ return new Promise((resolve) => {
85
+ process.nextTick(() => {
86
+ let data = [];
87
+ if (_linux || _freebsd || _openbsd || _netbsd || _darwin) {
88
+ let cmd = '';
89
+ if (_darwin) {
90
+ cmd = 'df -kP';
91
+ try {
92
+ macOsDisks = execSync('diskutil list').toString().split('\n').filter(line => {
93
+ return !line.startsWith('/') && line.indexOf(':') > 0;
94
+ });
95
+ } catch (e) {
96
+ macOsDisks = [];
97
+ }
98
+ }
99
+ if (_linux) { cmd = 'df -lkPTx squashfs | grep -E "^/|^.\\:"'; }
100
+ if (_freebsd || _openbsd || _netbsd) { cmd = 'df -lkPT'; }
101
+ exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) {
102
+ if (!error) {
103
+ let lines = stdout.toString().split('\n');
104
+ data = parseDf(lines);
105
+ if (callback) {
106
+ callback(data);
107
+ }
108
+ resolve(data);
109
+ } else {
110
+ exec('df -kPT', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
111
+ if (!error) {
112
+ let lines = stdout.toString().split('\n');
113
+ data = parseDf(lines);
114
+ }
115
+ if (callback) {
116
+ callback(data);
117
+ }
118
+ resolve(data);
119
+ });
120
+ }
121
+ });
122
+ }
123
+ if (_sunos) {
124
+ if (callback) { callback(data); }
125
+ resolve(data);
126
+ }
127
+ if (_windows) {
128
+ try {
129
+ util.wmic('logicaldisk get Caption,FileSystem,FreeSpace,Size').then((stdout) => {
130
+ let lines = stdout.split('\r\n').filter(line => line.trim() !== '').filter((line, idx) => idx > 0);
131
+ lines.forEach(function (line) {
132
+ if (line !== '') {
133
+ line = line.trim().split(/\s\s+/);
134
+ if (line.length >= 4 && parseInt(line[3], 10)) {
135
+ data.push({
136
+ fs: line[0],
137
+ type: line[1],
138
+ size: parseInt(line[3], 10),
139
+ used: parseInt(line[3], 10) - parseInt(line[2], 10),
140
+ available: parseInt(line[2], 10),
141
+ use: parseFloat(((100.0 * (parseInt(line[3]) - parseInt(line[2]))) / parseInt(line[3])).toFixed(2)),
142
+ mount: line[0]
143
+ });
144
+ }
145
+ }
146
+ });
147
+ if (callback) {
148
+ callback(data);
149
+ }
150
+ resolve(data);
151
+ });
152
+ } catch (e) {
153
+ if (callback) { callback(data); }
154
+ resolve(data);
155
+ }
156
+ }
157
+ });
158
+ });
159
+ }
160
+
161
+ exports.fsSize = fsSize;
162
+
163
+ // --------------------------
164
+ // FS - open files count
165
+
166
+ function fsOpenFiles(callback) {
167
+
168
+ return new Promise((resolve) => {
169
+ process.nextTick(() => {
170
+ const result = {
171
+ max: null,
172
+ allocated: null,
173
+ available: null
174
+ };
175
+ if (_freebsd || _openbsd || _netbsd || _darwin) {
176
+ let cmd = 'sysctl -a | grep \'kern.*files\'';
177
+ exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) {
178
+ if (!error) {
179
+ let lines = stdout.toString().split('\n');
180
+ result.max = parseInt(util.getValue(lines, 'kern.maxfiles', ':'), 10);
181
+ result.allocated = parseInt(util.getValue(lines, 'kern.num_files', ':'), 10);
182
+ result.available = result.max - result.allocated;
183
+ }
184
+ if (callback) {
185
+ callback(result);
186
+ }
187
+ resolve(result);
188
+ });
189
+ }
190
+ if (_linux) {
191
+ fs.readFile('/proc/sys/fs/file-nr', function (error, stdout) {
192
+ if (!error) {
193
+ let lines = stdout.toString().split('\n');
194
+ if (lines[0]) {
195
+ const parts = lines[0].replace(/\s+/g, ' ').split(' ');
196
+ if (parts.length === 3) {
197
+ result.allocated = parseInt(parts[0], 10);
198
+ result.available = parseInt(parts[1], 10);
199
+ result.max = parseInt(parts[2], 10);
200
+ if (!result.available) { result.available = result.max - result.allocated; }
201
+ }
202
+ }
203
+ if (callback) {
204
+ callback(result);
205
+ }
206
+ resolve(result);
207
+ } else {
208
+ fs.readFile('/proc/sys/fs/file-max', function (error, stdout) {
209
+ if (!error) {
210
+ let lines = stdout.toString().split('\n');
211
+ if (lines[0]) {
212
+ result.max = parseInt(lines[0], 10);
213
+ }
214
+ }
215
+ if (callback) {
216
+ callback(result);
217
+ }
218
+ resolve(result);
219
+ });
220
+ }
221
+ });
222
+ }
223
+ if (_sunos) {
224
+ if (callback) { callback(null); }
225
+ resolve(null);
226
+ }
227
+ if (_windows) {
228
+ if (callback) { callback(null); }
229
+ resolve(null);
230
+ }
231
+ });
232
+ });
233
+ }
234
+
235
+ exports.fsOpenFiles = fsOpenFiles;
236
+
237
+ // --------------------------
238
+ // disks
239
+
240
+ function parseBytes(s) {
241
+ return parseInt(s.substr(s.indexOf(' (') + 2, s.indexOf(' Bytes)') - 10));
242
+ }
243
+
244
+ function parseDevices(lines) {
245
+ let devices = [];
246
+ let i = 0;
247
+ lines.forEach(line => {
248
+ if (line.length > 0) {
249
+ if (line[0] === '*') {
250
+ i++;
251
+ } else {
252
+ let parts = line.split(':');
253
+ if (parts.length > 1) {
254
+ if (!devices[i]) {
255
+ devices[i] = {
256
+ name: '',
257
+ identifier: '',
258
+ type: 'disk',
259
+ fsType: '',
260
+ mount: '',
261
+ size: 0,
262
+ physical: 'HDD',
263
+ uuid: '',
264
+ label: '',
265
+ model: '',
266
+ serial: '',
267
+ removable: false,
268
+ protocol: ''
269
+ };
270
+ }
271
+ parts[0] = parts[0].trim().toUpperCase().replace(/ +/g, '');
272
+ parts[1] = parts[1].trim();
273
+ if ('DEVICEIDENTIFIER' === parts[0]) { devices[i].identifier = parts[1]; }
274
+ if ('DEVICENODE' === parts[0]) { devices[i].name = parts[1]; }
275
+ if ('VOLUMENAME' === parts[0]) {
276
+ if (parts[1].indexOf('Not applicable') === -1) { devices[i].label = parts[1]; }
277
+ }
278
+ if ('PROTOCOL' === parts[0]) { devices[i].protocol = parts[1]; }
279
+ if ('DISKSIZE' === parts[0]) { devices[i].size = parseBytes(parts[1]); }
280
+ if ('FILESYSTEMPERSONALITY' === parts[0]) { devices[i].fsType = parts[1]; }
281
+ if ('MOUNTPOINT' === parts[0]) { devices[i].mount = parts[1]; }
282
+ if ('VOLUMEUUID' === parts[0]) { devices[i].uuid = parts[1]; }
283
+ if ('READ-ONLYMEDIA' === parts[0] && parts[1] === 'Yes') { devices[i].physical = 'CD/DVD'; }
284
+ if ('SOLIDSTATE' === parts[0] && parts[1] === 'Yes') { devices[i].physical = 'SSD'; }
285
+ if ('VIRTUAL' === parts[0]) { devices[i].type = 'virtual'; }
286
+ if ('REMOVABLEMEDIA' === parts[0]) { devices[i].removable = (parts[1] === 'Removable'); }
287
+ if ('PARTITIONTYPE' === parts[0]) { devices[i].type = 'part'; }
288
+ if ('DEVICE/MEDIANAME' === parts[0]) { devices[i].model = parts[1]; }
289
+ }
290
+ }
291
+ }
292
+ });
293
+ return devices;
294
+ }
295
+
296
+ function parseBlk(lines) {
297
+ let data = [];
298
+
299
+ lines.filter(line => line !== '').forEach((line) => {
300
+ try {
301
+ line = decodeURIComponent(line.replace(/\\x/g, '%'));
302
+ line = line.replace(/\\/g, '\\\\');
303
+ let disk = JSON.parse(line);
304
+ data.push({
305
+ 'name': disk.name,
306
+ 'type': disk.type,
307
+ 'fsType': disk.fsType,
308
+ 'mount': disk.mountpoint,
309
+ 'size': parseInt(disk.size),
310
+ 'physical': (disk.type === 'disk' ? (disk.rota === '0' ? 'SSD' : 'HDD') : (disk.type === 'rom' ? 'CD/DVD' : '')),
311
+ 'uuid': disk.uuid,
312
+ 'label': disk.label,
313
+ 'model': disk.model,
314
+ 'serial': disk.serial,
315
+ 'removable': disk.rm === '1',
316
+ 'protocol': disk.tran,
317
+ 'group': disk.group,
318
+ });
319
+ } catch (e) {
320
+ util.noop();
321
+ }
322
+ });
323
+ data = util.unique(data);
324
+ data = util.sortByKey(data, ['type', 'name']);
325
+ return data;
326
+ }
327
+
328
+ function blkStdoutToObject(stdout) {
329
+ return stdout.toString()
330
+ .replace(/NAME=/g, '{"name":')
331
+ .replace(/FSTYPE=/g, ',"fsType":')
332
+ .replace(/TYPE=/g, ',"type":')
333
+ .replace(/SIZE=/g, ',"size":')
334
+ .replace(/MOUNTPOINT=/g, ',"mountpoint":')
335
+ .replace(/UUID=/g, ',"uuid":')
336
+ .replace(/ROTA=/g, ',"rota":')
337
+ .replace(/RO=/g, ',"ro":')
338
+ .replace(/RM=/g, ',"rm":')
339
+ .replace(/TRAN=/g, ',"tran":')
340
+ .replace(/SERIAL=/g, ',"serial":')
341
+ .replace(/LABEL=/g, ',"label":')
342
+ .replace(/MODEL=/g, ',"model":')
343
+ .replace(/OWNER=/g, ',"owner":')
344
+ .replace(/GROUP=/g, ',"group":')
345
+ .replace(/\n/g, '}\n');
346
+ }
347
+
348
+ function blockDevices(callback) {
349
+
350
+ return new Promise((resolve) => {
351
+ process.nextTick(() => {
352
+ let data = [];
353
+ if (_linux) {
354
+ // see https://wiki.ubuntuusers.de/lsblk/
355
+ // 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) {
356
+ exec('lsblk -bPo NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,UUID,ROTA,RO,RM,TRAN,SERIAL,LABEL,MODEL,OWNER 2>/dev/null', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
357
+ if (!error) {
358
+ let lines = blkStdoutToObject(stdout).split('\n');
359
+ data = parseBlk(lines);
360
+ if (callback) {
361
+ callback(data);
362
+ }
363
+ resolve(data);
364
+ } else {
365
+ exec('lsblk -bPo NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,UUID,ROTA,RO,RM,LABEL,MODEL,OWNER 2>/dev/null', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
366
+ if (!error) {
367
+ let lines = blkStdoutToObject(stdout).split('\n');
368
+ data = parseBlk(lines);
369
+ }
370
+ if (callback) {
371
+ callback(data);
372
+ }
373
+ resolve(data);
374
+ });
375
+ }
376
+ });
377
+ }
378
+ if (_darwin) {
379
+ exec('diskutil info -all', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
380
+ if (!error) {
381
+ let lines = stdout.toString().split('\n');
382
+ // parse lines into temp array of devices
383
+ data = parseDevices(lines);
384
+ }
385
+ if (callback) {
386
+ callback(data);
387
+ }
388
+ resolve(data);
389
+ });
390
+ }
391
+ if (_sunos) {
392
+ if (callback) { callback(data); }
393
+ resolve(data);
394
+ }
395
+ if (_windows) {
396
+ let drivetypes = ['Unknown', 'NoRoot', 'Removable', 'Local', 'Network', 'CD/DVD', 'RAM'];
397
+ try {
398
+ // util.wmic('logicaldisk get Caption,Description,DeviceID,DriveType,FileSystem,FreeSpace,Name,Size,VolumeName,VolumeSerialNumber /value').then((stdout, error) => {
399
+ util.powerShell('Get-CimInstance -ClassName Win32_LogicalDisk | Format-List *').then((stdout, error) => {
400
+ if (!error) {
401
+ let devices = stdout.toString().split(/\n\s*\n/);
402
+ devices.forEach(function (device) {
403
+ let lines = device.split('\r\n');
404
+ let drivetype = util.getValue(lines, 'drivetype', ':');
405
+ if (drivetype) {
406
+ data.push({
407
+ name: util.getValue(lines, 'name', ':'),
408
+ identifier: util.getValue(lines, 'caption', ':'),
409
+ type: 'disk',
410
+ fsType: util.getValue(lines, 'filesystem', ':').toLowerCase(),
411
+ mount: util.getValue(lines, 'caption', ':'),
412
+ size: util.getValue(lines, 'size', ':'),
413
+ physical: (drivetype >= 0 && drivetype <= 6) ? drivetypes[drivetype] : drivetypes[0],
414
+ uuid: util.getValue(lines, 'volumeserialnumber', ':'),
415
+ label: util.getValue(lines, 'volumename', ':'),
416
+ model: '',
417
+ serial: util.getValue(lines, 'volumeserialnumber', ':'),
418
+ removable: drivetype === '2',
419
+ protocol: ''
420
+ });
421
+ }
422
+ });
423
+ }
424
+ if (callback) {
425
+ callback(data);
426
+ }
427
+ resolve(data);
428
+ });
429
+ } catch (e) {
430
+ if (callback) { callback(data); }
431
+ resolve(data);
432
+ }
433
+ }
434
+ if (_freebsd || _openbsd || _netbsd) {
435
+ // will follow
436
+ if (callback) { callback(null); }
437
+ resolve(null);
438
+ }
439
+
440
+ });
441
+ });
442
+ }
443
+
444
+ exports.blockDevices = blockDevices;
445
+
446
+ // --------------------------
447
+ // FS - speed
448
+
449
+ function calcFsSpeed(rx, wx) {
450
+ let result = {
451
+ rx: 0,
452
+ wx: 0,
453
+ tx: 0,
454
+ rx_sec: null,
455
+ wx_sec: null,
456
+ tx_sec: null,
457
+ ms: 0
458
+ };
459
+
460
+ if (_fs_speed && _fs_speed.ms) {
461
+ result.rx = rx;
462
+ result.wx = wx;
463
+ result.tx = result.rx + result.wx;
464
+ result.ms = Date.now() - _fs_speed.ms;
465
+ result.rx_sec = (result.rx - _fs_speed.bytes_read) / (result.ms / 1000);
466
+ result.wx_sec = (result.wx - _fs_speed.bytes_write) / (result.ms / 1000);
467
+ result.tx_sec = result.rx_sec + result.wx_sec;
468
+ _fs_speed.rx_sec = result.rx_sec;
469
+ _fs_speed.wx_sec = result.wx_sec;
470
+ _fs_speed.tx_sec = result.tx_sec;
471
+ _fs_speed.bytes_read = result.rx;
472
+ _fs_speed.bytes_write = result.wx;
473
+ _fs_speed.bytes_overall = result.rx + result.wx;
474
+ _fs_speed.ms = Date.now();
475
+ _fs_speed.last_ms = result.ms;
476
+ } else {
477
+ result.rx = rx;
478
+ result.wx = wx;
479
+ result.tx = result.rx + result.wx;
480
+ _fs_speed.rx_sec = null;
481
+ _fs_speed.wx_sec = null;
482
+ _fs_speed.tx_sec = null;
483
+ _fs_speed.bytes_read = result.rx;
484
+ _fs_speed.bytes_write = result.wx;
485
+ _fs_speed.bytes_overall = result.rx + result.wx;
486
+ _fs_speed.ms = Date.now();
487
+ _fs_speed.last_ms = 0;
488
+ }
489
+ return result;
490
+ }
491
+
492
+ function fsStats(callback) {
493
+
494
+ return new Promise((resolve) => {
495
+ process.nextTick(() => {
496
+ if (_windows) {
497
+ return resolve(null);
498
+ }
499
+
500
+ let result = {
501
+ rx: 0,
502
+ wx: 0,
503
+ tx: 0,
504
+ rx_sec: null,
505
+ wx_sec: null,
506
+ tx_sec: null,
507
+ ms: 0
508
+ };
509
+
510
+ let rx = 0;
511
+ let wx = 0;
512
+ if ((_fs_speed && !_fs_speed.ms) || (_fs_speed && _fs_speed.ms && Date.now() - _fs_speed.ms >= 500)) {
513
+ if (_linux) {
514
+ // exec("df -k | grep /dev/", function(error, stdout) {
515
+ exec('lsblk -r 2>/dev/null | grep /', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
516
+ if (!error) {
517
+ let lines = stdout.toString().split('\n');
518
+ let fs_filter = [];
519
+ lines.forEach(function (line) {
520
+ if (line !== '') {
521
+ line = line.trim().split(' ');
522
+ if (fs_filter.indexOf(line[0]) === -1) { fs_filter.push(line[0]); }
523
+ }
524
+ });
525
+
526
+ let output = fs_filter.join('|');
527
+ exec('cat /proc/diskstats | egrep "' + output + '"', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
528
+ if (!error) {
529
+ let lines = stdout.toString().split('\n');
530
+ lines.forEach(function (line) {
531
+ line = line.trim();
532
+ if (line !== '') {
533
+ line = line.replace(/ +/g, ' ').split(' ');
534
+
535
+ rx += parseInt(line[5]) * 512;
536
+ wx += parseInt(line[9]) * 512;
537
+ }
538
+ });
539
+ result = calcFsSpeed(rx, wx);
540
+ }
541
+ if (callback) {
542
+ callback(result);
543
+ }
544
+ resolve(result);
545
+ });
546
+ } else {
547
+ if (callback) {
548
+ callback(result);
549
+ }
550
+ resolve(result);
551
+ }
552
+ });
553
+ }
554
+ if (_darwin) {
555
+ exec('ioreg -c IOBlockStorageDriver -k Statistics -r -w0 | sed -n "/IOBlockStorageDriver/,/Statistics/p" | grep "Statistics" | tr -cd "01234567890,\n"', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
556
+ if (!error) {
557
+ let lines = stdout.toString().split('\n');
558
+ lines.forEach(function (line) {
559
+ line = line.trim();
560
+ if (line !== '') {
561
+ line = line.split(',');
562
+
563
+ rx += parseInt(line[2]);
564
+ wx += parseInt(line[9]);
565
+ }
566
+ });
567
+ result = calcFsSpeed(rx, wx);
568
+ }
569
+ if (callback) {
570
+ callback(result);
571
+ }
572
+ resolve(result);
573
+ });
574
+ }
575
+ } else {
576
+ result.ms = _fs_speed.last_ms;
577
+ result.rx = _fs_speed.bytes_read;
578
+ result.wx = _fs_speed.bytes_write;
579
+ result.tx = _fs_speed.bytes_read + _fs_speed.bytes_write;
580
+ result.rx_sec = _fs_speed.rx_sec;
581
+ result.wx_sec = _fs_speed.wx_sec;
582
+ result.tx_sec = _fs_speed.tx_sec;
583
+ if (callback) {
584
+ callback(result);
585
+ }
586
+ resolve(result);
587
+ }
588
+ });
589
+ });
590
+ }
591
+
592
+ exports.fsStats = fsStats;
593
+
594
+ function calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime) {
595
+ let result = {
596
+ rIO: 0,
597
+ wIO: 0,
598
+ tIO: 0,
599
+ rIO_sec: null,
600
+ wIO_sec: null,
601
+ tIO_sec: null,
602
+ rWaitTime: 0,
603
+ wWaitTime: 0,
604
+ tWaitTime: 0,
605
+ rWaitPercent: null,
606
+ wWaitPercent: null,
607
+ tWaitPercent: null,
608
+ ms: 0
609
+ };
610
+ if (_disk_io && _disk_io.ms) {
611
+ result.rIO = rIO;
612
+ result.wIO = wIO;
613
+ result.tIO = rIO + wIO;
614
+ result.ms = Date.now() - _disk_io.ms;
615
+ result.rIO_sec = (result.rIO - _disk_io.rIO) / (result.ms / 1000);
616
+ result.wIO_sec = (result.wIO - _disk_io.wIO) / (result.ms / 1000);
617
+ result.tIO_sec = result.rIO_sec + result.wIO_sec;
618
+ result.rWaitTime = rWaitTime;
619
+ result.wWaitTime = wWaitTime;
620
+ result.tWaitTime = tWaitTime;
621
+ result.rWaitPercent = (result.rWaitTime - _disk_io.rWaitTime) * 100 / (result.ms);
622
+ result.wWaitPercent = (result.wWaitTime - _disk_io.wWaitTime) * 100 / (result.ms);
623
+ result.tWaitPercent = (result.tWaitTime - _disk_io.tWaitTime) * 100 / (result.ms);
624
+ _disk_io.rIO = rIO;
625
+ _disk_io.wIO = wIO;
626
+ _disk_io.rIO_sec = result.rIO_sec;
627
+ _disk_io.wIO_sec = result.wIO_sec;
628
+ _disk_io.tIO_sec = result.tIO_sec;
629
+ _disk_io.rWaitTime = rWaitTime;
630
+ _disk_io.wWaitTime = wWaitTime;
631
+ _disk_io.tWaitTime = tWaitTime;
632
+ _disk_io.rWaitPercent = result.rWaitPercent;
633
+ _disk_io.wWaitPercent = result.wWaitPercent;
634
+ _disk_io.tWaitPercent = result.tWaitPercent;
635
+ _disk_io.last_ms = result.ms;
636
+ _disk_io.ms = Date.now();
637
+ } else {
638
+ result.rIO = rIO;
639
+ result.wIO = wIO;
640
+ result.tIO = rIO + wIO;
641
+ result.rWaitTime = rWaitTime;
642
+ result.wWaitTime = wWaitTime;
643
+ result.tWaitTime = tWaitTime;
644
+ _disk_io.rIO = rIO;
645
+ _disk_io.wIO = wIO;
646
+ _disk_io.rIO_sec = null;
647
+ _disk_io.wIO_sec = null;
648
+ _disk_io.tIO_sec = null;
649
+ _disk_io.rWaitTime = rWaitTime;
650
+ _disk_io.wWaitTime = wWaitTime;
651
+ _disk_io.tWaitTime = tWaitTime;
652
+ _disk_io.rWaitPercent = null;
653
+ _disk_io.wWaitPercent = null;
654
+ _disk_io.tWaitPercent = null;
655
+ _disk_io.last_ms = 0;
656
+ _disk_io.ms = Date.now();
657
+ }
658
+ return result;
659
+ }
660
+
661
+ function disksIO(callback) {
662
+
663
+ return new Promise((resolve) => {
664
+ process.nextTick(() => {
665
+ if (_windows) {
666
+ return resolve(null);
667
+ }
668
+ if (_sunos) {
669
+ return resolve(null);
670
+ }
671
+
672
+ let result = {
673
+ rIO: 0,
674
+ wIO: 0,
675
+ tIO: 0,
676
+ rIO_sec: null,
677
+ wIO_sec: null,
678
+ tIO_sec: null,
679
+ rWaitTime: 0,
680
+ wWaitTime: 0,
681
+ tWaitTime: 0,
682
+ rWaitPercent: null,
683
+ wWaitPercent: null,
684
+ tWaitPercent: null,
685
+ ms: 0
686
+ };
687
+ let rIO = 0;
688
+ let wIO = 0;
689
+ let rWaitTime = 0;
690
+ let wWaitTime = 0;
691
+ let tWaitTime = 0;
692
+
693
+ if ((_disk_io && !_disk_io.ms) || (_disk_io && _disk_io.ms && Date.now() - _disk_io.ms >= 500)) {
694
+ if (_linux || _freebsd || _openbsd || _netbsd) {
695
+ // prints Block layer statistics for all mounted volumes
696
+ // 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";
697
+ // 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";
698
+ let cmd = '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';
699
+
700
+ exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) {
701
+ if (!error) {
702
+ let lines = stdout.split('\n');
703
+ lines.forEach(function (line) {
704
+ // ignore empty lines
705
+ if (!line) { return; }
706
+
707
+ // sum r/wIO of all disks to compute all disks IO
708
+ let stats = line.split(';');
709
+ rIO += parseInt(stats[0]);
710
+ wIO += parseInt(stats[4]);
711
+ rWaitTime += parseInt(stats[3]);
712
+ wWaitTime += parseInt(stats[7]);
713
+ tWaitTime += parseInt(stats[10]);
714
+ });
715
+ result = calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime);
716
+
717
+ if (callback) {
718
+ callback(result);
719
+ }
720
+ resolve(result);
721
+ } else {
722
+ if (callback) {
723
+ callback(result);
724
+ }
725
+ resolve(result);
726
+ }
727
+ });
728
+ }
729
+ if (_darwin) {
730
+ exec('ioreg -c IOBlockStorageDriver -k Statistics -r -w0 | sed -n "/IOBlockStorageDriver/,/Statistics/p" | grep "Statistics" | tr -cd "01234567890,\n"', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
731
+ if (!error) {
732
+ let lines = stdout.toString().split('\n');
733
+ lines.forEach(function (line) {
734
+ line = line.trim();
735
+ if (line !== '') {
736
+ line = line.split(',');
737
+
738
+ rIO += parseInt(line[10]);
739
+ wIO += parseInt(line[0]);
740
+ }
741
+ });
742
+ result = calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime);
743
+ }
744
+ if (callback) {
745
+ callback(result);
746
+ }
747
+ resolve(result);
748
+ });
749
+ }
750
+ } else {
751
+ result.rIO = _disk_io.rIO;
752
+ result.wIO = _disk_io.wIO;
753
+ result.tIO = _disk_io.rIO + _disk_io.wIO;
754
+ result.ms = _disk_io.last_ms;
755
+ result.rIO_sec = _disk_io.rIO_sec;
756
+ result.wIO_sec = _disk_io.wIO_sec;
757
+ result.tIO_sec = _disk_io.tIO_sec;
758
+ result.rWaitTime = _disk_io.rWaitTime;
759
+ result.wWaitTime = _disk_io.wWaitTime;
760
+ result.tWaitTime = _disk_io.tWaitTime;
761
+ result.rWaitPercent = _disk_io.rWaitPercent;
762
+ result.wWaitPercent = _disk_io.wWaitPercent;
763
+ result.tWaitPercent = _disk_io.tWaitPercent;
764
+ if (callback) {
765
+ callback(result);
766
+ }
767
+ resolve(result);
768
+ }
769
+ });
770
+ });
771
+ }
772
+
773
+ exports.disksIO = disksIO;
774
+
775
+ function diskLayout(callback) {
776
+
777
+ function getVendorFromModel(model) {
778
+ const diskManufacturers = [
779
+ { pattern: '^WESTERN.+', manufacturer: 'Western Digital' },
780
+ { pattern: '^WDC.+', manufacturer: 'Western Digital' },
781
+ { pattern: 'WD.+', manufacturer: 'Western Digital' },
782
+ { pattern: '^TOSHIBA.+', manufacturer: 'Toshiba' },
783
+ { pattern: '^HITACHI.+', manufacturer: 'Hitachi' },
784
+ { pattern: '^IC.+', manufacturer: 'Hitachi' },
785
+ { pattern: '^HTS.+', manufacturer: 'Hitachi' },
786
+ { pattern: '^SANDISK.+', manufacturer: 'SanDisk' },
787
+ { pattern: '^KINGSTON.+', manufacturer: 'Kingston Technology' },
788
+ { pattern: '^SONY.+', manufacturer: 'Sony' },
789
+ { pattern: '^TRANSCEND.+', manufacturer: 'Transcend' },
790
+ { pattern: 'SAMSUNG.+', manufacturer: 'Samsung' },
791
+ { pattern: '^ST(?!I\\ ).+', manufacturer: 'Seagate' },
792
+ { pattern: '^STI\\ .+', manufacturer: 'SimpleTech' },
793
+ { pattern: '^D...-.+', manufacturer: 'IBM' },
794
+ { pattern: '^IBM.+', manufacturer: 'IBM' },
795
+ { pattern: '^FUJITSU.+', manufacturer: 'Fujitsu' },
796
+ { pattern: '^MP.+', manufacturer: 'Fujitsu' },
797
+ { pattern: '^MK.+', manufacturer: 'Toshiba' },
798
+ { pattern: '^MAXTOR.+', manufacturer: 'Maxtor' },
799
+ { pattern: '^Pioneer.+', manufacturer: 'Pioneer' },
800
+ { pattern: '^PHILIPS.+', manufacturer: 'Philips' },
801
+ { pattern: '^QUANTUM.+', manufacturer: 'Quantum Technology' },
802
+ { pattern: 'FIREBALL.+', manufacturer: 'Quantum Technology' },
803
+ { pattern: '^VBOX.+', manufacturer: 'VirtualBox' },
804
+ { pattern: 'CORSAIR.+', manufacturer: 'Corsair Components' },
805
+ { pattern: 'CRUCIAL.+', manufacturer: 'Crucial' },
806
+ { pattern: 'ECM.+', manufacturer: 'ECM' },
807
+ { pattern: 'INTEL.+', manufacturer: 'INTEL' },
808
+ { pattern: '.+EVO', manufacturer: 'Samsung' },
809
+ { pattern: 'APPLE.+', manufacturer: 'Apple' },
810
+ ];
811
+
812
+ let result = '';
813
+ if (model) {
814
+ model = model.toUpperCase();
815
+ diskManufacturers.forEach((manufacturer) => {
816
+ const re = RegExp(manufacturer.pattern);
817
+ if (re.test(model)) { result = manufacturer.manufacturer; }
818
+ });
819
+ }
820
+ return result;
821
+ }
822
+
823
+ return new Promise((resolve) => {
824
+ process.nextTick(() => {
825
+
826
+ const commitResult = res => {
827
+ for (let i = 0; i < res.length; i++) {
828
+ delete res[i].BSDName;
829
+ }
830
+ if (callback) {
831
+ callback(res);
832
+ }
833
+ resolve(res);
834
+ };
835
+
836
+ let result = [];
837
+ let cmd = '';
838
+
839
+ if (_linux) {
840
+ let cmdFullSmart = '';
841
+
842
+ exec('export LC_ALL=C; lsblk -ablJO 2>/dev/null; unset LC_ALL', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
843
+ if (!error) {
844
+ try {
845
+ const out = stdout.toString().trim();
846
+ let devices = [];
847
+ try {
848
+ const outJSON = JSON.parse(out);
849
+ if (outJSON && {}.hasOwnProperty.call(outJSON, 'blockdevices')) {
850
+ devices = outJSON.blockdevices.filter(item => { return (item.type === 'disk') && item.size > 0 && (item.model !== null || (item.mountpoint === null && item.label === null && item.fsType === null && item.parttype === null)); });
851
+ }
852
+ } catch (e) {
853
+ // fallback to older version of lsblk
854
+ const out2 = execSync('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').toString();
855
+ let lines = blkStdoutToObject(out2).split('\n');
856
+ const data = parseBlk(lines);
857
+ devices = data.filter(item => { return (item.type === 'disk') && item.size > 0 && ((item.model !== null && item.model !== '') || (item.mount === '' && item.label === '' && item.fsType === '')); });
858
+ }
859
+ devices.forEach((device) => {
860
+ let mediumType = '';
861
+ const BSDName = '/dev/' + device.name;
862
+ const logical = device.name;
863
+ try {
864
+ mediumType = execSync('cat /sys/block/' + logical + '/queue/rotational 2>/dev/null').toString().split('\n')[0];
865
+ } catch (e) {
866
+ util.noop();
867
+ }
868
+ let interfaceType = device.tran ? device.tran.toUpperCase().trim() : '';
869
+ if (interfaceType === 'NVME') {
870
+ mediumType = '2';
871
+ interfaceType = 'PCIe';
872
+ }
873
+ result.push({
874
+ device: BSDName,
875
+ type: (mediumType === '0' ? 'SSD' : (mediumType === '1' ? 'HD' : (mediumType === '2' ? 'NVMe' : (device.model && device.model.indexOf('SSD') > -1 ? 'SSD' : (device.model && device.model.indexOf('NVM') > -1 ? 'NVMe' : 'HD'))))),
876
+ name: device.model || '',
877
+ vendor: getVendorFromModel(device.model) || (device.vendor ? device.vendor.trim() : ''),
878
+ size: device.size || 0,
879
+ bytesPerSector: null,
880
+ totalCylinders: null,
881
+ totalHeads: null,
882
+ totalSectors: null,
883
+ totalTracks: null,
884
+ tracksPerCylinder: null,
885
+ sectorsPerTrack: null,
886
+ firmwareRevision: device.rev ? device.rev.trim() : '',
887
+ serialNum: device.serial ? device.serial.trim() : '',
888
+ interfaceType: interfaceType,
889
+ smartStatus: 'unknown',
890
+ temperature: null,
891
+ BSDName: BSDName
892
+ });
893
+ cmd += `printf "\n${BSDName}|"; smartctl -H ${BSDName} | grep overall;`;
894
+ cmdFullSmart += `${cmdFullSmart ? 'printf ",";' : ''}smartctl -a -j ${BSDName};`;
895
+ });
896
+ } catch (e) {
897
+ util.noop();
898
+ }
899
+ }
900
+ // check S.M.A.R.T. status
901
+ if (cmdFullSmart) {
902
+ exec(cmdFullSmart, { maxBuffer: 1024 * 1024 }, function (error, stdout) {
903
+ try {
904
+ const data = JSON.parse(`[${stdout}]`);
905
+ data.forEach(disk => {
906
+ const diskBSDName = disk.smartctl.argv[disk.smartctl.argv.length - 1];
907
+
908
+ for (let i = 0; i < result.length; i++) {
909
+ if (result[i].BSDName === diskBSDName) {
910
+ result[i].smartStatus = (disk.smart_status.passed ? 'Ok' : (disk.smart_status.passed === false ? 'Predicted Failure' : 'unknown'));
911
+ if (disk.temperature && disk.temperature.current) {
912
+ result[i].temperature = disk.temperature.current;
913
+ }
914
+ result[i].smartData = disk;
915
+ }
916
+ }
917
+ });
918
+ commitResult(result);
919
+ } catch (e) {
920
+ if (cmd) {
921
+ cmd = cmd + 'printf "\n"';
922
+ exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) {
923
+ let lines = stdout.toString().split('\n');
924
+ lines.forEach(line => {
925
+ if (line) {
926
+ let parts = line.split('|');
927
+ if (parts.length === 2) {
928
+ let BSDName = parts[0];
929
+ parts[1] = parts[1].trim();
930
+ let parts2 = parts[1].split(':');
931
+ if (parts2.length === 2) {
932
+ parts2[1] = parts2[1].trim();
933
+ let status = parts2[1].toLowerCase();
934
+ for (let i = 0; i < result.length; i++) {
935
+ if (result[i].BSDName === BSDName) {
936
+ result[i].smartStatus = (status === 'passed' ? 'Ok' : (status === 'failed!' ? 'Predicted Failure' : 'unknown'));
937
+ }
938
+ }
939
+ }
940
+ }
941
+ }
942
+ });
943
+ commitResult(result);
944
+ });
945
+ } else {
946
+ commitResult(result);
947
+ }
948
+ }
949
+ });
950
+ } else {
951
+ commitResult(result);
952
+ }
953
+ });
954
+ }
955
+ if (_freebsd || _openbsd || _netbsd) {
956
+ if (callback) { callback(result); }
957
+ resolve(result);
958
+ }
959
+ if (_sunos) {
960
+ if (callback) { callback(result); }
961
+ resolve(result);
962
+ }
963
+ if (_darwin) {
964
+ exec('system_profiler SPSerialATADataType SPNVMeDataType SPUSBDataType', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
965
+ if (!error) {
966
+ // split by type:
967
+ let lines = stdout.toString().split('\n');
968
+ let linesSATA = [];
969
+ let linesNVMe = [];
970
+ let linesUSB = [];
971
+ let dataType = 'SATA';
972
+ lines.forEach(line => {
973
+ if (line === 'NVMExpress:') { dataType = 'NVMe'; }
974
+ else if (line === 'USB:') { dataType = 'USB'; }
975
+ else if (line === 'SATA/SATA Express:') { dataType = 'SATA'; }
976
+ else if (dataType === 'SATA') { linesSATA.push(line); }
977
+ else if (dataType === 'NVMe') { linesNVMe.push(line); }
978
+ else if (dataType === 'USB') { linesUSB.push(line); }
979
+ });
980
+ try {
981
+ // Serial ATA Drives
982
+ let devices = linesSATA.join('\n').split(' Physical Interconnect: ');
983
+ devices.shift();
984
+ devices.forEach(function (device) {
985
+ device = 'InterfaceType: ' + device;
986
+ let lines = device.split('\n');
987
+ const mediumType = util.getValue(lines, 'Medium Type', ':', true).trim();
988
+ const sizeStr = util.getValue(lines, 'capacity', ':', true).trim();
989
+ const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim();
990
+ if (sizeStr) {
991
+ let sizeValue = 0;
992
+ if (sizeStr.indexOf('(') >= 0) {
993
+ sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, '').replace(/\s/g, ''));
994
+ }
995
+ if (!sizeValue) {
996
+ sizeValue = parseInt(sizeStr);
997
+ }
998
+ if (sizeValue) {
999
+ const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase();
1000
+ result.push({
1001
+ device: BSDName,
1002
+ type: mediumType.startsWith('Solid') ? 'SSD' : 'HD',
1003
+ name: util.getValue(lines, 'Model', ':', true).trim(),
1004
+ vendor: getVendorFromModel(util.getValue(lines, 'Model', ':', true).trim()) || util.getValue(lines, 'Manufacturer', ':', true),
1005
+ size: sizeValue,
1006
+ bytesPerSector: null,
1007
+ totalCylinders: null,
1008
+ totalHeads: null,
1009
+ totalSectors: null,
1010
+ totalTracks: null,
1011
+ tracksPerCylinder: null,
1012
+ sectorsPerTrack: null,
1013
+ firmwareRevision: util.getValue(lines, 'Revision', ':', true).trim(),
1014
+ serialNum: util.getValue(lines, 'Serial Number', ':', true).trim(),
1015
+ interfaceType: util.getValue(lines, 'InterfaceType', ':', true).trim(),
1016
+ smartStatus: smartStatusString === 'verified' ? 'OK' : smartStatusString || 'unknown',
1017
+ temperature: null,
1018
+ BSDName: BSDName
1019
+ });
1020
+ cmd = cmd + 'printf "\n' + BSDName + '|"; diskutil info /dev/' + BSDName + ' | grep SMART;';
1021
+ }
1022
+ }
1023
+ });
1024
+ } catch (e) {
1025
+ util.noop();
1026
+ }
1027
+
1028
+ // NVME Drives
1029
+ try {
1030
+ let devices = linesNVMe.join('\n').split('\n\n Capacity:');
1031
+ devices.shift();
1032
+ devices.forEach(function (device) {
1033
+ device = '!Capacity: ' + device;
1034
+ let lines = device.split('\n');
1035
+ const linkWidth = util.getValue(lines, 'link width', ':', true).trim();
1036
+ const sizeStr = util.getValue(lines, '!capacity', ':', true).trim();
1037
+ const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim();
1038
+ if (sizeStr) {
1039
+ let sizeValue = 0;
1040
+ if (sizeStr.indexOf('(') >= 0) {
1041
+ sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, '').replace(/\s/g, ''));
1042
+ }
1043
+ if (!sizeValue) {
1044
+ sizeValue = parseInt(sizeStr);
1045
+ }
1046
+ if (sizeValue) {
1047
+ const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase();
1048
+ result.push({
1049
+ device: BSDName,
1050
+ type: 'NVMe',
1051
+ name: util.getValue(lines, 'Model', ':', true).trim(),
1052
+ vendor: getVendorFromModel(util.getValue(lines, 'Model', ':', true).trim()),
1053
+ size: sizeValue,
1054
+ bytesPerSector: null,
1055
+ totalCylinders: null,
1056
+ totalHeads: null,
1057
+ totalSectors: null,
1058
+ totalTracks: null,
1059
+ tracksPerCylinder: null,
1060
+ sectorsPerTrack: null,
1061
+ firmwareRevision: util.getValue(lines, 'Revision', ':', true).trim(),
1062
+ serialNum: util.getValue(lines, 'Serial Number', ':', true).trim(),
1063
+ interfaceType: ('PCIe ' + linkWidth).trim(),
1064
+ smartStatus: smartStatusString === 'verified' ? 'OK' : smartStatusString || 'unknown',
1065
+ temperature: null,
1066
+ BSDName: BSDName
1067
+ });
1068
+ cmd = cmd + 'printf "\n' + BSDName + '|"; diskutil info /dev/' + BSDName + ' | grep SMART;';
1069
+ }
1070
+ }
1071
+ });
1072
+ } catch (e) {
1073
+ util.noop();
1074
+ }
1075
+ // USB Drives
1076
+ try {
1077
+ let devices = linesUSB.join('\n').replaceAll('Media:\n ', 'Model:').split('\n\n Product ID:');
1078
+ devices.shift();
1079
+ devices.forEach(function (device) {
1080
+ let lines = device.split('\n');
1081
+ const sizeStr = util.getValue(lines, 'Capacity', ':', true).trim();
1082
+ const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim();
1083
+ if (sizeStr) {
1084
+ let sizeValue = 0;
1085
+ if (sizeStr.indexOf('(') >= 0) {
1086
+ sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, '').replace(/\s/g, ''));
1087
+ }
1088
+ if (!sizeValue) {
1089
+ sizeValue = parseInt(sizeStr);
1090
+ }
1091
+ if (sizeValue) {
1092
+ const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase();
1093
+ result.push({
1094
+ device: BSDName,
1095
+ type: 'USB',
1096
+ name: util.getValue(lines, 'Model', ':', true).trim().replaceAll(':', ''),
1097
+ vendor: getVendorFromModel(util.getValue(lines, 'Model', ':', true).trim()),
1098
+ size: sizeValue,
1099
+ bytesPerSector: null,
1100
+ totalCylinders: null,
1101
+ totalHeads: null,
1102
+ totalSectors: null,
1103
+ totalTracks: null,
1104
+ tracksPerCylinder: null,
1105
+ sectorsPerTrack: null,
1106
+ firmwareRevision: util.getValue(lines, 'Revision', ':', true).trim(),
1107
+ serialNum: util.getValue(lines, 'Serial Number', ':', true).trim(),
1108
+ interfaceType: 'USB',
1109
+ smartStatus: smartStatusString === 'verified' ? 'OK' : smartStatusString || 'unknown',
1110
+ temperature: null,
1111
+ BSDName: BSDName
1112
+ });
1113
+ cmd = cmd + 'printf "\n' + BSDName + '|"; diskutil info /dev/' + BSDName + ' | grep SMART;';
1114
+ }
1115
+ }
1116
+ });
1117
+ } catch (e) {
1118
+ util.noop();
1119
+ }
1120
+ if (cmd) {
1121
+ cmd = cmd + 'printf "\n"';
1122
+ exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) {
1123
+ let lines = stdout.toString().split('\n');
1124
+ lines.forEach(line => {
1125
+ if (line) {
1126
+ let parts = line.split('|');
1127
+ if (parts.length === 2) {
1128
+ let BSDName = parts[0];
1129
+ parts[1] = parts[1].trim();
1130
+ let parts2 = parts[1].split(':');
1131
+ if (parts2.length === 2) {
1132
+ parts2[1] = parts2[1].trim();
1133
+ let status = parts2[1].toLowerCase();
1134
+ for (let i = 0; i < result.length; i++) {
1135
+ if (result[i].BSDName === BSDName) {
1136
+ result[i].smartStatus = (status === 'not supported' ? 'not supported' : (status === 'verified' ? 'Ok' : (status === 'failing' ? 'Predicted Failure' : 'unknown')));
1137
+ }
1138
+ }
1139
+ }
1140
+ }
1141
+ }
1142
+ });
1143
+ for (let i = 0; i < result.length; i++) {
1144
+ delete result[i].BSDName;
1145
+ }
1146
+ if (callback) {
1147
+ callback(result);
1148
+ }
1149
+ resolve(result);
1150
+ });
1151
+ } else {
1152
+ for (let i = 0; i < result.length; i++) {
1153
+ delete result[i].BSDName;
1154
+ }
1155
+ if (callback) {
1156
+ callback(result);
1157
+ }
1158
+ resolve(result);
1159
+ }
1160
+ }
1161
+ });
1162
+ }
1163
+ if (_windows) {
1164
+ try {
1165
+ const workload = [];
1166
+ workload.push(util.powerShell('Get-WmiObject Win32_DiskDrive | fl *'));
1167
+ workload.push(util.powerShell('Get-PhysicalDisk | Format-List'));
1168
+ if (util.smartMonToolsInstalled()) {
1169
+ try {
1170
+ const smartDev = JSON.parse(execSync('smartctl --scan -j'));
1171
+ if (smartDev && smartDev.devices && smartDev.devices.length > 0) {
1172
+ smartDev.devices.forEach((dev) => {
1173
+ workload.push(execPromiseSave(`smartctl -j -a ${dev.name}`, util.execOptsWin));
1174
+ });
1175
+ }
1176
+ } catch (e) {
1177
+ util.noop();
1178
+ }
1179
+ }
1180
+ util.promiseAll(
1181
+ workload
1182
+ ).then(data => {
1183
+ let devices = data.results[0].toString().split(/\n\s*\n/);
1184
+ devices.forEach(function (device) {
1185
+ let lines = device.split('\r\n');
1186
+ const size = util.getValue(lines, 'Size', ':').trim();
1187
+ const status = util.getValue(lines, 'Status', ':').trim().toLowerCase();
1188
+ if (size) {
1189
+ result.push({
1190
+ device: util.getValue(lines, 'PNPDeviceId', ':'),
1191
+ type: device.indexOf('SSD') > -1 ? 'SSD' : 'HD', // just a starting point ... better: MSFT_PhysicalDisk - Media Type ... see below
1192
+ name: util.getValue(lines, 'Caption', ':'),
1193
+ vendor: getVendorFromModel(util.getValue(lines, 'Caption', ':', true).trim()),
1194
+ size: parseInt(size),
1195
+ bytesPerSector: parseInt(util.getValue(lines, 'BytesPerSector', ':')),
1196
+ totalCylinders: parseInt(util.getValue(lines, 'TotalCylinders', ':')),
1197
+ totalHeads: parseInt(util.getValue(lines, 'TotalHeads', ':')),
1198
+ totalSectors: parseInt(util.getValue(lines, 'TotalSectors', ':')),
1199
+ totalTracks: parseInt(util.getValue(lines, 'TotalTracks', ':')),
1200
+ tracksPerCylinder: parseInt(util.getValue(lines, 'TracksPerCylinder', ':')),
1201
+ sectorsPerTrack: parseInt(util.getValue(lines, 'SectorsPerTrack', ':')),
1202
+ firmwareRevision: util.getValue(lines, 'FirmwareRevision', ':').trim(),
1203
+ serialNum: util.getValue(lines, 'SerialNumber', ':').trim(),
1204
+ interfaceType: util.getValue(lines, 'InterfaceType', ':').trim(),
1205
+ smartStatus: (status === 'ok' ? 'Ok' : (status === 'degraded' ? 'Degraded' : (status === 'pred fail' ? 'Predicted Failure' : 'Unknown'))),
1206
+ temperature: null,
1207
+ });
1208
+ }
1209
+ });
1210
+ devices = data.results[1].split(/\n\s*\n/);
1211
+ devices.forEach(function (device) {
1212
+ let lines = device.split('\r\n');
1213
+ const serialNum = util.getValue(lines, 'SerialNumber', ':').trim();
1214
+ const name = util.getValue(lines, 'FriendlyName', ':').trim().replace('Msft ', 'Microsoft');
1215
+ const size = util.getValue(lines, 'Size', ':').trim();
1216
+ const model = util.getValue(lines, 'Model', ':').trim();
1217
+ const interfaceType = util.getValue(lines, 'BusType', ':').trim();
1218
+ let mediaType = util.getValue(lines, 'MediaType', ':').trim();
1219
+ if (mediaType === '3' || mediaType === 'HDD') { mediaType = 'HD'; }
1220
+ if (mediaType === '4') { mediaType = 'SSD'; }
1221
+ if (mediaType === '5') { mediaType = 'SCM'; }
1222
+ if (mediaType === 'Unspecified' && (model.toLowerCase().indexOf('virtual') > -1 || model.toLowerCase().indexOf('vbox') > -1)) { mediaType = 'Virtual'; }
1223
+ if (size) {
1224
+ let i = util.findObjectByKey(result, 'serialNum', serialNum);
1225
+ if (i === -1 || serialNum === '') {
1226
+ i = util.findObjectByKey(result, 'name', name);
1227
+ }
1228
+ if (i != -1) {
1229
+ result[i].type = mediaType;
1230
+ result[i].interfaceType = interfaceType;
1231
+ }
1232
+ }
1233
+ });
1234
+ // S.M.A.R.T
1235
+ data.results.shift();
1236
+ data.results.shift();
1237
+ data.results.forEach((smartStr) => {
1238
+ const smartData = JSON.parse(smartStr);
1239
+ if (smartData.serial_number) {
1240
+ const serialNum = smartData.serial_number;
1241
+ let i = util.findObjectByKey(result, 'serialNum', serialNum);
1242
+ if (i != -1) {
1243
+ result[i].smartStatus = (smartData.smart_status.passed ? 'Ok' : (smartData.smart_status.passed === false ? 'Predicted Failure' : 'unknown'));
1244
+ if (smartData.temperature && smartData.temperature.current) {
1245
+ result[i].temperature = smartData.temperature.current;
1246
+ }
1247
+ result[i].smartData = smartData;
1248
+ }
1249
+ }
1250
+ });
1251
+ if (callback) {
1252
+ callback(result);
1253
+ }
1254
+ resolve(result);
1255
+ });
1256
+ } catch (e) {
1257
+ if (callback) { callback(result); }
1258
+ resolve(result);
1259
+ }
1260
+ }
1261
+ });
1262
+ });
1263
+ }
1264
+
1265
+ exports.diskLayout = diskLayout;