Repositorio del curso CCOM4030 el semestre B91 del proyecto Artesanías con el Instituto de Cultura

filesystem.js 39KB

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