systeminformation 5.9.4 → 5.9.8

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,1264 +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
- if (!error) {
400
- let devices = stdout.toString().split(/\n\s*\n/);
401
- devices.forEach(function (device) {
402
- let lines = device.split('\r\n');
403
- let drivetype = util.getValue(lines, 'drivetype', '=');
404
- if (drivetype) {
405
- data.push({
406
- name: util.getValue(lines, 'name', '='),
407
- identifier: util.getValue(lines, 'caption', '='),
408
- type: 'disk',
409
- fsType: util.getValue(lines, 'filesystem', '=').toLowerCase(),
410
- mount: util.getValue(lines, 'caption', '='),
411
- size: util.getValue(lines, 'size', '='),
412
- physical: (drivetype >= 0 && drivetype <= 6) ? drivetypes[drivetype] : drivetypes[0],
413
- uuid: util.getValue(lines, 'volumeserialnumber', '='),
414
- label: util.getValue(lines, 'volumename', '='),
415
- model: '',
416
- serial: util.getValue(lines, 'volumeserialnumber', '='),
417
- removable: drivetype === '2',
418
- protocol: ''
419
- });
420
- }
421
- });
422
- }
423
- if (callback) {
424
- callback(data);
425
- }
426
- resolve(data);
427
- });
428
- } catch (e) {
429
- if (callback) { callback(data); }
430
- resolve(data);
431
- }
432
- }
433
- if (_freebsd || _openbsd || _netbsd) {
434
- // will follow
435
- if (callback) { callback(null); }
436
- resolve(null);
437
- }
438
-
439
- });
440
- });
441
- }
442
-
443
- exports.blockDevices = blockDevices;
444
-
445
- // --------------------------
446
- // FS - speed
447
-
448
- function calcFsSpeed(rx, wx) {
449
- let result = {
450
- rx: 0,
451
- wx: 0,
452
- tx: 0,
453
- rx_sec: null,
454
- wx_sec: null,
455
- tx_sec: null,
456
- ms: 0
457
- };
458
-
459
- if (_fs_speed && _fs_speed.ms) {
460
- result.rx = rx;
461
- result.wx = wx;
462
- result.tx = result.rx + result.wx;
463
- result.ms = Date.now() - _fs_speed.ms;
464
- result.rx_sec = (result.rx - _fs_speed.bytes_read) / (result.ms / 1000);
465
- result.wx_sec = (result.wx - _fs_speed.bytes_write) / (result.ms / 1000);
466
- result.tx_sec = result.rx_sec + result.wx_sec;
467
- _fs_speed.rx_sec = result.rx_sec;
468
- _fs_speed.wx_sec = result.wx_sec;
469
- _fs_speed.tx_sec = result.tx_sec;
470
- _fs_speed.bytes_read = result.rx;
471
- _fs_speed.bytes_write = result.wx;
472
- _fs_speed.bytes_overall = result.rx + result.wx;
473
- _fs_speed.ms = Date.now();
474
- _fs_speed.last_ms = result.ms;
475
- } else {
476
- result.rx = rx;
477
- result.wx = wx;
478
- result.tx = result.rx + result.wx;
479
- _fs_speed.rx_sec = null;
480
- _fs_speed.wx_sec = null;
481
- _fs_speed.tx_sec = null;
482
- _fs_speed.bytes_read = result.rx;
483
- _fs_speed.bytes_write = result.wx;
484
- _fs_speed.bytes_overall = result.rx + result.wx;
485
- _fs_speed.ms = Date.now();
486
- _fs_speed.last_ms = 0;
487
- }
488
- return result;
489
- }
490
-
491
- function fsStats(callback) {
492
-
493
- return new Promise((resolve) => {
494
- process.nextTick(() => {
495
- if (_windows) {
496
- return resolve(null);
497
- }
498
-
499
- let result = {
500
- rx: 0,
501
- wx: 0,
502
- tx: 0,
503
- rx_sec: null,
504
- wx_sec: null,
505
- tx_sec: null,
506
- ms: 0
507
- };
508
-
509
- let rx = 0;
510
- let wx = 0;
511
- if ((_fs_speed && !_fs_speed.ms) || (_fs_speed && _fs_speed.ms && Date.now() - _fs_speed.ms >= 500)) {
512
- if (_linux) {
513
- // exec("df -k | grep /dev/", function(error, stdout) {
514
- exec('lsblk -r 2>/dev/null | grep /', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
515
- if (!error) {
516
- let lines = stdout.toString().split('\n');
517
- let fs_filter = [];
518
- lines.forEach(function (line) {
519
- if (line !== '') {
520
- line = line.trim().split(' ');
521
- if (fs_filter.indexOf(line[0]) === -1) { fs_filter.push(line[0]); }
522
- }
523
- });
524
-
525
- let output = fs_filter.join('|');
526
- exec('cat /proc/diskstats | egrep "' + output + '"', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
527
- if (!error) {
528
- let lines = stdout.toString().split('\n');
529
- lines.forEach(function (line) {
530
- line = line.trim();
531
- if (line !== '') {
532
- line = line.replace(/ +/g, ' ').split(' ');
533
-
534
- rx += parseInt(line[5]) * 512;
535
- wx += parseInt(line[9]) * 512;
536
- }
537
- });
538
- result = calcFsSpeed(rx, wx);
539
- }
540
- if (callback) {
541
- callback(result);
542
- }
543
- resolve(result);
544
- });
545
- } else {
546
- if (callback) {
547
- callback(result);
548
- }
549
- resolve(result);
550
- }
551
- });
552
- }
553
- if (_darwin) {
554
- 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) {
555
- if (!error) {
556
- let lines = stdout.toString().split('\n');
557
- lines.forEach(function (line) {
558
- line = line.trim();
559
- if (line !== '') {
560
- line = line.split(',');
561
-
562
- rx += parseInt(line[2]);
563
- wx += parseInt(line[9]);
564
- }
565
- });
566
- result = calcFsSpeed(rx, wx);
567
- }
568
- if (callback) {
569
- callback(result);
570
- }
571
- resolve(result);
572
- });
573
- }
574
- } else {
575
- result.ms = _fs_speed.last_ms;
576
- result.rx = _fs_speed.bytes_read;
577
- result.wx = _fs_speed.bytes_write;
578
- result.tx = _fs_speed.bytes_read + _fs_speed.bytes_write;
579
- result.rx_sec = _fs_speed.rx_sec;
580
- result.wx_sec = _fs_speed.wx_sec;
581
- result.tx_sec = _fs_speed.tx_sec;
582
- if (callback) {
583
- callback(result);
584
- }
585
- resolve(result);
586
- }
587
- });
588
- });
589
- }
590
-
591
- exports.fsStats = fsStats;
592
-
593
- function calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime) {
594
- let result = {
595
- rIO: 0,
596
- wIO: 0,
597
- tIO: 0,
598
- rIO_sec: null,
599
- wIO_sec: null,
600
- tIO_sec: null,
601
- rWaitTime: 0,
602
- wWaitTime: 0,
603
- tWaitTime: 0,
604
- rWaitPercent: null,
605
- wWaitPercent: null,
606
- tWaitPercent: null,
607
- ms: 0
608
- };
609
- if (_disk_io && _disk_io.ms) {
610
- result.rIO = rIO;
611
- result.wIO = wIO;
612
- result.tIO = rIO + wIO;
613
- result.ms = Date.now() - _disk_io.ms;
614
- result.rIO_sec = (result.rIO - _disk_io.rIO) / (result.ms / 1000);
615
- result.wIO_sec = (result.wIO - _disk_io.wIO) / (result.ms / 1000);
616
- result.tIO_sec = result.rIO_sec + result.wIO_sec;
617
- result.rWaitTime = rWaitTime;
618
- result.wWaitTime = wWaitTime;
619
- result.tWaitTime = tWaitTime;
620
- result.rWaitPercent = (result.rWaitTime - _disk_io.rWaitTime) * 100 / (result.ms);
621
- result.wWaitPercent = (result.wWaitTime - _disk_io.wWaitTime) * 100 / (result.ms);
622
- result.tWaitPercent = (result.tWaitTime - _disk_io.tWaitTime) * 100 / (result.ms);
623
- _disk_io.rIO = rIO;
624
- _disk_io.wIO = wIO;
625
- _disk_io.rIO_sec = result.rIO_sec;
626
- _disk_io.wIO_sec = result.wIO_sec;
627
- _disk_io.tIO_sec = result.tIO_sec;
628
- _disk_io.rWaitTime = rWaitTime;
629
- _disk_io.wWaitTime = wWaitTime;
630
- _disk_io.tWaitTime = tWaitTime;
631
- _disk_io.rWaitPercent = result.rWaitPercent;
632
- _disk_io.wWaitPercent = result.wWaitPercent;
633
- _disk_io.tWaitPercent = result.tWaitPercent;
634
- _disk_io.last_ms = result.ms;
635
- _disk_io.ms = Date.now();
636
- } else {
637
- result.rIO = rIO;
638
- result.wIO = wIO;
639
- result.tIO = rIO + wIO;
640
- result.rWaitTime = rWaitTime;
641
- result.wWaitTime = wWaitTime;
642
- result.tWaitTime = tWaitTime;
643
- _disk_io.rIO = rIO;
644
- _disk_io.wIO = wIO;
645
- _disk_io.rIO_sec = null;
646
- _disk_io.wIO_sec = null;
647
- _disk_io.tIO_sec = null;
648
- _disk_io.rWaitTime = rWaitTime;
649
- _disk_io.wWaitTime = wWaitTime;
650
- _disk_io.tWaitTime = tWaitTime;
651
- _disk_io.rWaitPercent = null;
652
- _disk_io.wWaitPercent = null;
653
- _disk_io.tWaitPercent = null;
654
- _disk_io.last_ms = 0;
655
- _disk_io.ms = Date.now();
656
- }
657
- return result;
658
- }
659
-
660
- function disksIO(callback) {
661
-
662
- return new Promise((resolve) => {
663
- process.nextTick(() => {
664
- if (_windows) {
665
- return resolve(null);
666
- }
667
- if (_sunos) {
668
- return resolve(null);
669
- }
670
-
671
- let result = {
672
- rIO: 0,
673
- wIO: 0,
674
- tIO: 0,
675
- rIO_sec: null,
676
- wIO_sec: null,
677
- tIO_sec: null,
678
- rWaitTime: 0,
679
- wWaitTime: 0,
680
- tWaitTime: 0,
681
- rWaitPercent: null,
682
- wWaitPercent: null,
683
- tWaitPercent: null,
684
- ms: 0
685
- };
686
- let rIO = 0;
687
- let wIO = 0;
688
- let rWaitTime = 0;
689
- let wWaitTime = 0;
690
- let tWaitTime = 0;
691
-
692
- if ((_disk_io && !_disk_io.ms) || (_disk_io && _disk_io.ms && Date.now() - _disk_io.ms >= 500)) {
693
- if (_linux || _freebsd || _openbsd || _netbsd) {
694
- // prints Block layer statistics for all mounted volumes
695
- // 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";
696
- // 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";
697
- 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';
698
-
699
- exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) {
700
- if (!error) {
701
- let lines = stdout.split('\n');
702
- lines.forEach(function (line) {
703
- // ignore empty lines
704
- if (!line) { return; }
705
-
706
- // sum r/wIO of all disks to compute all disks IO
707
- let stats = line.split(';');
708
- rIO += parseInt(stats[0]);
709
- wIO += parseInt(stats[4]);
710
- rWaitTime += parseInt(stats[3]);
711
- wWaitTime += parseInt(stats[7]);
712
- tWaitTime += parseInt(stats[10]);
713
- });
714
- result = calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime);
715
-
716
- if (callback) {
717
- callback(result);
718
- }
719
- resolve(result);
720
- } else {
721
- if (callback) {
722
- callback(result);
723
- }
724
- resolve(result);
725
- }
726
- });
727
- }
728
- if (_darwin) {
729
- 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) {
730
- if (!error) {
731
- let lines = stdout.toString().split('\n');
732
- lines.forEach(function (line) {
733
- line = line.trim();
734
- if (line !== '') {
735
- line = line.split(',');
736
-
737
- rIO += parseInt(line[10]);
738
- wIO += parseInt(line[0]);
739
- }
740
- });
741
- result = calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime);
742
- }
743
- if (callback) {
744
- callback(result);
745
- }
746
- resolve(result);
747
- });
748
- }
749
- } else {
750
- result.rIO = _disk_io.rIO;
751
- result.wIO = _disk_io.wIO;
752
- result.tIO = _disk_io.rIO + _disk_io.wIO;
753
- result.ms = _disk_io.last_ms;
754
- result.rIO_sec = _disk_io.rIO_sec;
755
- result.wIO_sec = _disk_io.wIO_sec;
756
- result.tIO_sec = _disk_io.tIO_sec;
757
- result.rWaitTime = _disk_io.rWaitTime;
758
- result.wWaitTime = _disk_io.wWaitTime;
759
- result.tWaitTime = _disk_io.tWaitTime;
760
- result.rWaitPercent = _disk_io.rWaitPercent;
761
- result.wWaitPercent = _disk_io.wWaitPercent;
762
- result.tWaitPercent = _disk_io.tWaitPercent;
763
- if (callback) {
764
- callback(result);
765
- }
766
- resolve(result);
767
- }
768
- });
769
- });
770
- }
771
-
772
- exports.disksIO = disksIO;
773
-
774
- function diskLayout(callback) {
775
-
776
- function getVendorFromModel(model) {
777
- const diskManufacturers = [
778
- { pattern: '^WESTERN.+', manufacturer: 'Western Digital' },
779
- { pattern: '^WDC.+', manufacturer: 'Western Digital' },
780
- { pattern: 'WD.+', manufacturer: 'Western Digital' },
781
- { pattern: '^TOSHIBA.+', manufacturer: 'Toshiba' },
782
- { pattern: '^HITACHI.+', manufacturer: 'Hitachi' },
783
- { pattern: '^IC.+', manufacturer: 'Hitachi' },
784
- { pattern: '^HTS.+', manufacturer: 'Hitachi' },
785
- { pattern: '^SANDISK.+', manufacturer: 'SanDisk' },
786
- { pattern: '^KINGSTON.+', manufacturer: 'Kingston Technonogy' },
787
- { pattern: '^SONY.+', manufacturer: 'Sony' },
788
- { pattern: '^TRANSCEND.+', manufacturer: 'Transcend' },
789
- { pattern: 'SAMSUNG.+', manufacturer: 'Samsung' },
790
- { pattern: '^ST(?!I\\ ).+', manufacturer: 'Seagate' },
791
- { pattern: '^STI\\ .+', manufacturer: 'SimpleTech' },
792
- { pattern: '^D...-.+', manufacturer: 'IBM' },
793
- { pattern: '^IBM.+', manufacturer: 'IBM' },
794
- { pattern: '^FUJITSU.+', manufacturer: 'Fujitsu' },
795
- { pattern: '^MP.+', manufacturer: 'Fujitsu' },
796
- { pattern: '^MK.+', manufacturer: 'Toshiba' },
797
- { pattern: '^MAXTOR.+', manufacturer: 'Maxtor' },
798
- { pattern: '^Pioneer.+', manufacturer: 'Pioneer' },
799
- { pattern: '^PHILIPS.+', manufacturer: 'Philips' },
800
- { pattern: '^QUANTUM.+', manufacturer: 'Quantum Technology' },
801
- { pattern: 'FIREBALL.+', manufacturer: 'Quantum Technology' },
802
- { pattern: '^VBOX.+', manufacturer: 'VirtualBox' },
803
- { pattern: 'CORSAIR.+', manufacturer: 'Corsair Components' },
804
- { pattern: 'CRUCIAL.+', manufacturer: 'Crucial' },
805
- { pattern: 'ECM.+', manufacturer: 'ECM' },
806
- { pattern: 'INTEL.+', manufacturer: 'INTEL' },
807
- { pattern: '.+EVO', manufacturer: 'Samsung' },
808
- { pattern: 'APPLE.+', manufacturer: 'Apple' },
809
- ];
810
-
811
- let result = '';
812
- if (model) {
813
- model = model.toUpperCase();
814
- diskManufacturers.forEach((manufacturer) => {
815
- const re = RegExp(manufacturer.pattern);
816
- if (re.test(model)) { result = manufacturer.manufacturer; }
817
- });
818
- }
819
- return result;
820
- }
821
-
822
- return new Promise((resolve) => {
823
- process.nextTick(() => {
824
-
825
- const commitResult = res => {
826
- for (let i = 0; i < res.length; i++) {
827
- delete res[i].BSDName;
828
- }
829
- if (callback) {
830
- callback(res);
831
- }
832
- resolve(res);
833
- };
834
-
835
- let result = [];
836
- let cmd = '';
837
-
838
- if (_linux) {
839
- let cmdFullSmart = '';
840
-
841
- exec('export LC_ALL=C; lsblk -ablJO 2>/dev/null; unset LC_ALL', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
842
- if (!error) {
843
- try {
844
- const out = stdout.toString().trim();
845
- let devices = [];
846
- try {
847
- const outJSON = JSON.parse(out);
848
- if (outJSON && {}.hasOwnProperty.call(outJSON, 'blockdevices')) {
849
- 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)); });
850
- }
851
- } catch (e) {
852
- // fallback to older version of lsblk
853
- 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();
854
- let lines = blkStdoutToObject(out2).split('\n');
855
- const data = parseBlk(lines);
856
- devices = data.filter(item => { return (item.type === 'disk') && item.size > 0 && ((item.model !== null && item.model !== '') || (item.mount === '' && item.label === '' && item.fsType === '')); });
857
- }
858
- devices.forEach((device) => {
859
- let mediumType = '';
860
- const BSDName = '/dev/' + device.name;
861
- const logical = device.name;
862
- try {
863
- mediumType = execSync('cat /sys/block/' + logical + '/queue/rotational 2>/dev/null').toString().split('\n')[0];
864
- } catch (e) {
865
- util.noop();
866
- }
867
- let interfaceType = device.tran ? device.tran.toUpperCase().trim() : '';
868
- if (interfaceType === 'NVME') {
869
- mediumType = '2';
870
- interfaceType = 'PCIe';
871
- }
872
- result.push({
873
- device: BSDName,
874
- 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'))))),
875
- name: device.model || '',
876
- vendor: getVendorFromModel(device.model) || (device.vendor ? device.vendor.trim() : ''),
877
- size: device.size || 0,
878
- bytesPerSector: null,
879
- totalCylinders: null,
880
- totalHeads: null,
881
- totalSectors: null,
882
- totalTracks: null,
883
- tracksPerCylinder: null,
884
- sectorsPerTrack: null,
885
- firmwareRevision: device.rev ? device.rev.trim() : '',
886
- serialNum: device.serial ? device.serial.trim() : '',
887
- interfaceType: interfaceType,
888
- smartStatus: 'unknown',
889
- temperature: null,
890
- BSDName: BSDName
891
- });
892
- cmd += `printf "\n${BSDName}|"; smartctl -H ${BSDName} | grep overall;`;
893
- cmdFullSmart += `${cmdFullSmart ? 'printf ",";' : ''}smartctl -a -j ${BSDName};`;
894
- });
895
- } catch (e) {
896
- util.noop();
897
- }
898
- }
899
- // check S.M.A.R.T. status
900
- if (cmdFullSmart) {
901
- exec(cmdFullSmart, { maxBuffer: 1024 * 1024 }, function (error, stdout) {
902
- try {
903
- const data = JSON.parse(`[${stdout}]`);
904
- data.forEach(disk => {
905
- const diskBSDName = disk.smartctl.argv[disk.smartctl.argv.length - 1];
906
-
907
- for (let i = 0; i < result.length; i++) {
908
- if (result[i].BSDName === diskBSDName) {
909
- result[i].smartStatus = (disk.smart_status.passed ? 'Ok' : (disk.smart_status.passed === false ? 'Predicted Failure' : 'unknown'));
910
- if (disk.temperature && disk.temperature.current) {
911
- result[i].temperature = disk.temperature.current;
912
- }
913
- result[i].smartData = disk;
914
- }
915
- }
916
- });
917
- commitResult(result);
918
- } catch (e) {
919
- if (cmd) {
920
- cmd = cmd + 'printf "\n"';
921
- exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) {
922
- let lines = stdout.toString().split('\n');
923
- lines.forEach(line => {
924
- if (line) {
925
- let parts = line.split('|');
926
- if (parts.length === 2) {
927
- let BSDName = parts[0];
928
- parts[1] = parts[1].trim();
929
- let parts2 = parts[1].split(':');
930
- if (parts2.length === 2) {
931
- parts2[1] = parts2[1].trim();
932
- let status = parts2[1].toLowerCase();
933
- for (let i = 0; i < result.length; i++) {
934
- if (result[i].BSDName === BSDName) {
935
- result[i].smartStatus = (status === 'passed' ? 'Ok' : (status === 'failed!' ? 'Predicted Failure' : 'unknown'));
936
- }
937
- }
938
- }
939
- }
940
- }
941
- });
942
- commitResult(result);
943
- });
944
- } else {
945
- commitResult(result);
946
- }
947
- }
948
- });
949
- } else {
950
- commitResult(result);
951
- }
952
- });
953
- }
954
- if (_freebsd || _openbsd || _netbsd) {
955
- if (callback) { callback(result); }
956
- resolve(result);
957
- }
958
- if (_sunos) {
959
- if (callback) { callback(result); }
960
- resolve(result);
961
- }
962
- if (_darwin) {
963
- exec('system_profiler SPSerialATADataType SPNVMeDataType SPUSBDataType', { maxBuffer: 1024 * 1024 }, function (error, stdout) {
964
- if (!error) {
965
- // split by type:
966
- let lines = stdout.toString().split('\n');
967
- let linesSATA = [];
968
- let linesNVMe = [];
969
- let linesUSB = [];
970
- let dataType = 'SATA';
971
- lines.forEach(line => {
972
- if (line === 'NVMExpress:') { dataType = 'NVMe'; }
973
- else if (line === 'USB:') { dataType = 'USB'; }
974
- else if (line === 'SATA/SATA Express:') { dataType = 'SATA'; }
975
- else if (dataType === 'SATA') { linesSATA.push(line); }
976
- else if (dataType === 'NVMe') { linesNVMe.push(line); }
977
- else if (dataType === 'USB') { linesUSB.push(line); }
978
- });
979
- try {
980
- // Serial ATA Drives
981
- let devices = linesSATA.join('\n').split(' Physical Interconnect: ');
982
- devices.shift();
983
- devices.forEach(function (device) {
984
- device = 'InterfaceType: ' + device;
985
- let lines = device.split('\n');
986
- const mediumType = util.getValue(lines, 'Medium Type', ':', true).trim();
987
- const sizeStr = util.getValue(lines, 'capacity', ':', true).trim();
988
- const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim();
989
- if (sizeStr) {
990
- let sizeValue = 0;
991
- if (sizeStr.indexOf('(') >= 0) {
992
- sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, '').replace(/\s/g, ''));
993
- }
994
- if (!sizeValue) {
995
- sizeValue = parseInt(sizeStr);
996
- }
997
- if (sizeValue) {
998
- const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase();
999
- result.push({
1000
- device: BSDName,
1001
- type: mediumType.startsWith('Solid') ? 'SSD' : 'HD',
1002
- name: util.getValue(lines, 'Model', ':', true).trim(),
1003
- vendor: getVendorFromModel(util.getValue(lines, 'Model', ':', true).trim()) || util.getValue(lines, 'Manufacturer', ':', true),
1004
- size: sizeValue,
1005
- bytesPerSector: null,
1006
- totalCylinders: null,
1007
- totalHeads: null,
1008
- totalSectors: null,
1009
- totalTracks: null,
1010
- tracksPerCylinder: null,
1011
- sectorsPerTrack: null,
1012
- firmwareRevision: util.getValue(lines, 'Revision', ':', true).trim(),
1013
- serialNum: util.getValue(lines, 'Serial Number', ':', true).trim(),
1014
- interfaceType: util.getValue(lines, 'InterfaceType', ':', true).trim(),
1015
- smartStatus: smartStatusString === 'verified' ? 'OK' : smartStatusString || 'unknown',
1016
- temperature: null,
1017
- BSDName: BSDName
1018
- });
1019
- cmd = cmd + 'printf "\n' + BSDName + '|"; diskutil info /dev/' + BSDName + ' | grep SMART;';
1020
- }
1021
- }
1022
- });
1023
- } catch (e) {
1024
- util.noop();
1025
- }
1026
-
1027
- // NVME Drives
1028
- try {
1029
- let devices = linesNVMe.join('\n').split('\n\n Capacity:');
1030
- devices.shift();
1031
- devices.forEach(function (device) {
1032
- device = '!Capacity: ' + device;
1033
- let lines = device.split('\n');
1034
- const linkWidth = util.getValue(lines, 'link width', ':', true).trim();
1035
- const sizeStr = util.getValue(lines, '!capacity', ':', true).trim();
1036
- const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim();
1037
- if (sizeStr) {
1038
- let sizeValue = 0;
1039
- if (sizeStr.indexOf('(') >= 0) {
1040
- sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, '').replace(/\s/g, ''));
1041
- }
1042
- if (!sizeValue) {
1043
- sizeValue = parseInt(sizeStr);
1044
- }
1045
- if (sizeValue) {
1046
- const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase();
1047
- result.push({
1048
- device: BSDName,
1049
- type: 'NVMe',
1050
- name: util.getValue(lines, 'Model', ':', true).trim(),
1051
- vendor: getVendorFromModel(util.getValue(lines, 'Model', ':', true).trim()),
1052
- size: sizeValue,
1053
- bytesPerSector: null,
1054
- totalCylinders: null,
1055
- totalHeads: null,
1056
- totalSectors: null,
1057
- totalTracks: null,
1058
- tracksPerCylinder: null,
1059
- sectorsPerTrack: null,
1060
- firmwareRevision: util.getValue(lines, 'Revision', ':', true).trim(),
1061
- serialNum: util.getValue(lines, 'Serial Number', ':', true).trim(),
1062
- interfaceType: ('PCIe ' + linkWidth).trim(),
1063
- smartStatus: smartStatusString === 'verified' ? 'OK' : smartStatusString || 'unknown',
1064
- temperature: null,
1065
- BSDName: BSDName
1066
- });
1067
- cmd = cmd + 'printf "\n' + BSDName + '|"; diskutil info /dev/' + BSDName + ' | grep SMART;';
1068
- }
1069
- }
1070
- });
1071
- } catch (e) {
1072
- util.noop();
1073
- }
1074
- // USB Drives
1075
- try {
1076
- let devices = linesUSB.join('\n').replaceAll('Media:\n ', 'Model:').split('\n\n Product ID:');
1077
- devices.shift();
1078
- devices.forEach(function (device) {
1079
- let lines = device.split('\n');
1080
- const sizeStr = util.getValue(lines, 'Capacity', ':', true).trim();
1081
- const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim();
1082
- if (sizeStr) {
1083
- let sizeValue = 0;
1084
- if (sizeStr.indexOf('(') >= 0) {
1085
- sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, '').replace(/\s/g, ''));
1086
- }
1087
- if (!sizeValue) {
1088
- sizeValue = parseInt(sizeStr);
1089
- }
1090
- if (sizeValue) {
1091
- const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase();
1092
- result.push({
1093
- device: BSDName,
1094
- type: 'USB',
1095
- name: util.getValue(lines, 'Model', ':', true).trim().replaceAll(':', ''),
1096
- vendor: getVendorFromModel(util.getValue(lines, 'Model', ':', true).trim()),
1097
- size: sizeValue,
1098
- bytesPerSector: null,
1099
- totalCylinders: null,
1100
- totalHeads: null,
1101
- totalSectors: null,
1102
- totalTracks: null,
1103
- tracksPerCylinder: null,
1104
- sectorsPerTrack: null,
1105
- firmwareRevision: util.getValue(lines, 'Revision', ':', true).trim(),
1106
- serialNum: util.getValue(lines, 'Serial Number', ':', true).trim(),
1107
- interfaceType: 'USB',
1108
- smartStatus: smartStatusString === 'verified' ? 'OK' : smartStatusString || 'unknown',
1109
- temperature: null,
1110
- BSDName: BSDName
1111
- });
1112
- cmd = cmd + 'printf "\n' + BSDName + '|"; diskutil info /dev/' + BSDName + ' | grep SMART;';
1113
- }
1114
- }
1115
- });
1116
- } catch (e) {
1117
- util.noop();
1118
- }
1119
- if (cmd) {
1120
- cmd = cmd + 'printf "\n"';
1121
- exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) {
1122
- let lines = stdout.toString().split('\n');
1123
- lines.forEach(line => {
1124
- if (line) {
1125
- let parts = line.split('|');
1126
- if (parts.length === 2) {
1127
- let BSDName = parts[0];
1128
- parts[1] = parts[1].trim();
1129
- let parts2 = parts[1].split(':');
1130
- if (parts2.length === 2) {
1131
- parts2[1] = parts2[1].trim();
1132
- let status = parts2[1].toLowerCase();
1133
- for (let i = 0; i < result.length; i++) {
1134
- if (result[i].BSDName === BSDName) {
1135
- result[i].smartStatus = (status === 'not supported' ? 'not supported' : (status === 'verified' ? 'Ok' : (status === 'failing' ? 'Predicted Failure' : 'unknown')));
1136
- }
1137
- }
1138
- }
1139
- }
1140
- }
1141
- });
1142
- for (let i = 0; i < result.length; i++) {
1143
- delete result[i].BSDName;
1144
- }
1145
- if (callback) {
1146
- callback(result);
1147
- }
1148
- resolve(result);
1149
- });
1150
- } else {
1151
- for (let i = 0; i < result.length; i++) {
1152
- delete result[i].BSDName;
1153
- }
1154
- if (callback) {
1155
- callback(result);
1156
- }
1157
- resolve(result);
1158
- }
1159
- }
1160
- });
1161
- }
1162
- if (_windows) {
1163
- try {
1164
- const workload = [];
1165
- workload.push(util.wmic('diskdrive get /value'));
1166
- workload.push(util.powerShell('Get-PhysicalDisk | Format-List'));
1167
- if (util.smartMonToolsInstalled()) {
1168
- try {
1169
- const smartDev = JSON.parse(execSync('smartctl --scan -j'));
1170
- if (smartDev && smartDev.devices && smartDev.devices.length > 0) {
1171
- smartDev.devices.forEach((dev) => {
1172
- workload.push(execPromiseSave(`smartctl -j -a ${dev.name}`, util.execOptsWin));
1173
- });
1174
- }
1175
- } catch (e) {
1176
- util.noop();
1177
- }
1178
- }
1179
- util.promiseAll(
1180
- workload
1181
- ).then(data => {
1182
- let devices = data.results[0].toString().split(/\n\s*\n/);
1183
- devices.forEach(function (device) {
1184
- let lines = device.split('\r\n');
1185
- const size = util.getValue(lines, 'Size', '=').trim();
1186
- const status = util.getValue(lines, 'Status', '=').trim().toLowerCase();
1187
- if (size) {
1188
- result.push({
1189
- device: util.getValue(lines, 'PNPDeviceId', '='),
1190
- type: device.indexOf('SSD') > -1 ? 'SSD' : 'HD', // just a starting point ... better: MSFT_PhysicalDisk - Media Type ... see below
1191
- name: util.getValue(lines, 'Caption', '='),
1192
- vendor: getVendorFromModel(util.getValue(lines, 'Caption', '=', true).trim()),
1193
- size: parseInt(size),
1194
- bytesPerSector: parseInt(util.getValue(lines, 'BytesPerSector', '=')),
1195
- totalCylinders: parseInt(util.getValue(lines, 'TotalCylinders', '=')),
1196
- totalHeads: parseInt(util.getValue(lines, 'TotalHeads', '=')),
1197
- totalSectors: parseInt(util.getValue(lines, 'TotalSectors', '=')),
1198
- totalTracks: parseInt(util.getValue(lines, 'TotalTracks', '=')),
1199
- tracksPerCylinder: parseInt(util.getValue(lines, 'TracksPerCylinder', '=')),
1200
- sectorsPerTrack: parseInt(util.getValue(lines, 'SectorsPerTrack', '=')),
1201
- firmwareRevision: util.getValue(lines, 'FirmwareRevision', '=').trim(),
1202
- serialNum: util.getValue(lines, 'SerialNumber', '=').trim(),
1203
- interfaceType: util.getValue(lines, 'InterfaceType', '=').trim(),
1204
- smartStatus: (status === 'ok' ? 'Ok' : (status === 'degraded' ? 'Degraded' : (status === 'pred fail' ? 'Predicted Failure' : 'Unknown'))),
1205
- temperature: null,
1206
- });
1207
- }
1208
- });
1209
- devices = data.results[1].split(/\n\s*\n/);
1210
- devices.forEach(function (device) {
1211
- let lines = device.split('\r\n');
1212
- const serialNum = util.getValue(lines, 'SerialNumber', ':').trim();
1213
- const name = util.getValue(lines, 'FriendlyName', ':').trim().replace('Msft ', 'Microsoft');
1214
- const size = util.getValue(lines, 'Size', ':').trim();
1215
- const model = util.getValue(lines, 'Model', ':').trim();
1216
- const interfaceType = util.getValue(lines, 'BusType', ':').trim();
1217
- let mediaType = util.getValue(lines, 'MediaType', ':').trim();
1218
- if (mediaType === '3' || mediaType === 'HDD') { mediaType = 'HD'; }
1219
- if (mediaType === '4') { mediaType = 'SSD'; }
1220
- if (mediaType === '5') { mediaType = 'SCM'; }
1221
- if (mediaType === 'Unspecified' && (model.toLowerCase().indexOf('virtual') > -1 || model.toLowerCase().indexOf('vbox') > -1)) { mediaType = 'Virtual'; }
1222
- if (size) {
1223
- let i = util.findObjectByKey(result, 'serialNum', serialNum);
1224
- if (i === -1 || serialNum === '') {
1225
- i = util.findObjectByKey(result, 'name', name);
1226
- }
1227
- if (i != -1) {
1228
- result[i].type = mediaType;
1229
- result[i].interfaceType = interfaceType;
1230
- }
1231
- }
1232
- });
1233
- // S.M.A.R.T
1234
- data.results.shift();
1235
- data.results.shift();
1236
- data.results.forEach((smartStr) => {
1237
- const smartData = JSON.parse(smartStr);
1238
- if (smartData.serial_number) {
1239
- const serialNum = smartData.serial_number;
1240
- let i = util.findObjectByKey(result, 'serialNum', serialNum);
1241
- if (i != -1) {
1242
- result[i].smartStatus = (smartData.smart_status.passed ? 'Ok' : (smartData.smart_status.passed === false ? 'Predicted Failure' : 'unknown'));
1243
- if (smartData.temperature && smartData.temperature.current) {
1244
- result[i].temperature = smartData.temperature.current;
1245
- }
1246
- result[i].smartData = smartData;
1247
- }
1248
- }
1249
- });
1250
- if (callback) {
1251
- callback(result);
1252
- }
1253
- resolve(result);
1254
- });
1255
- } catch (e) {
1256
- if (callback) { callback(result); }
1257
- resolve(result);
1258
- }
1259
- }
1260
- });
1261
- });
1262
- }
1263
-
1264
- 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 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;