diff options
author | Minteck <contact@minteck.org> | 2022-07-05 13:40:01 +0200 |
---|---|---|
committer | Minteck <contact@minteck.org> | 2022-07-05 13:40:01 +0200 |
commit | b74e9708f0adcc5c80095e432e8051b01b6255d9 (patch) | |
tree | e4d2fec7ac3718f97966b10f7dc12a2b4d36d115 | |
download | strawberry-boot-b74e9708f0adcc5c80095e432e8051b01b6255d9.tar.gz strawberry-boot-b74e9708f0adcc5c80095e432e8051b01b6255d9.tar.bz2 strawberry-boot-b74e9708f0adcc5c80095e432e8051b01b6255d9.zip |
70 files changed, 19724 insertions, 0 deletions
diff --git a/.DS_Store b/.DS_Store Binary files differnew file mode 100644 index 0000000..acfdfa2 --- /dev/null +++ b/.DS_Store diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..b58b603 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/deployment.xml b/.idea/deployment.xml new file mode 100644 index 0000000..97e75c4 --- /dev/null +++ b/.idea/deployment.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="PublishConfigData" autoUpload="Always" serverName="Strawberry Development Kit" remoteFilesAllowedToDisappearOnAutoupload="false"> + <serverData> + <paths name="Strawberry Development Kit"> + <serverdata> + <mappings> + <mapping deploy="/home/strawberry/strawberry" local="$PROJECT_DIR$" web="/" /> + </mappings> + </serverdata> + </paths> + </serverData> + <option name="myAutoUpload" value="ALWAYS" /> + </component> +</project>
\ No newline at end of file diff --git a/.idea/jsLibraryMappings.xml b/.idea/jsLibraryMappings.xml new file mode 100644 index 0000000..d23208f --- /dev/null +++ b/.idea/jsLibraryMappings.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="JavaScriptLibraryMappings"> + <includedPredefinedLibrary name="Node.js Core" /> + </component> +</project>
\ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..66e7b8b --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/.idea/strasberry-boot.iml" filepath="$PROJECT_DIR$/.idea/strasberry-boot.iml" /> + </modules> + </component> +</project>
\ No newline at end of file diff --git a/.idea/strasberry-boot.iml b/.idea/strasberry-boot.iml new file mode 100644 index 0000000..0c8867d --- /dev/null +++ b/.idea/strasberry-boot.iml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="WEB_MODULE" version="4"> + <component name="NewModuleRootManager"> + <content url="file://$MODULE_DIR$"> + <excludeFolder url="file://$MODULE_DIR$/temp" /> + <excludeFolder url="file://$MODULE_DIR$/.tmp" /> + <excludeFolder url="file://$MODULE_DIR$/tmp" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> +</module>
\ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1360d74 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# Strawberry Bootloader + +## Error Codes + +| Error Code | Meaning | Fix | +|--------------|---------------------------------------------------------------------------------------|---------------------------------------------------------------------| +| `0x0000000F` | Booting from a Startup Disk is not implemented in this version | Update your device's firmware | +| `0x0000000A` | This Startup Disk is formatted correctly but cannot be booted from | Use a valid and official Startup Disk | +| `0x0000001B` | This Startup Disk contains a version of Strawberry OS that is too new for this device | Use a Startup Disk that contains a correct version of Strawberry OS | +| `0x0000002B` | This Startup Disk contains a version of Strawberry OS that is too old for this device | Use a Startup Disk that contains a correct version of Strawberry OS | diff --git a/boot.wav b/boot.wav Binary files differnew file mode 100644 index 0000000..9ab6a49 --- /dev/null +++ b/boot.wav diff --git a/bootloader b/bootloader new file mode 100644 index 0000000..85bf5a7 --- /dev/null +++ b/bootloader @@ -0,0 +1,196 @@ +#!/usr/bin/env node + +require('hide-terminal-cursor')(); + +if (process.stdout.rows <= 50 || process.stdout.columns <= 130) { + console.clear(); + require('child_process').execSync("aplay /home/strawberry/strawberry/failure.wav 2> /dev/null"); + process.exit(); +} + +console.clear(); +require('child_process').execSync("aplay /home/strawberry/strawberry/boot.wav 2> /dev/null"); + +if (require('fs').existsSync("/home/strawberry/strawberry/restart")) { + require('fs').rmSync("/home/strawberry/strawberry/restart"); +} + +global.osVersion = "1.1.7"; +global.osVersionCode = 9; +global.version = "1.5.6"; +global.message = "Please insert a Startup Disk containing Strawberry OS " + osVersion + " or later."; +global.mounted = false; + +(async () => { + function error(text, text2) { + clearInterval(loadInterval); + clearInterval(loadInterval2); + console.clear(); + + let out = ""; + + global.vertical = Math.round(process.stdout.rows / 2); + let horizontal = Math.round(process.stdout.columns / 2 - (1 + (text.length / 2))); + let horizontal2 = Math.round(process.stdout.columns / 2 - (1 + (text2.length / 2))); + + for (let n = 0; n < vertical; n++) { + out += "\n" + " ".repeat(process.stdout.columns); + } + + out += " ".repeat(horizontal) + text + " ".repeat(process.stdout.columns - horizontal - text.length) + "\n" + " ".repeat(horizontal2) + text2 + " ".repeat(process.stdout.columns - horizontal2 - text2.length); + + for (let n = 0; n < vertical - 2; n++) { + out += "\n" + " ".repeat(process.stdout.columns); + } + + console.log(chalk.bgRed.whiteBright(out)); + if (fs.existsSync("/home/strawberry/strawberry/restart")) { + fs.rmSync("/home/strawberry/strawberry/restart"); + } else { + require('child_process').execSync("aplay /home/strawberry/strawberry/crash.wav 2> /dev/null"); + } + } + + process.on('uncaughtException', (error) => { + let text2; + let text; + + if (error.message === "User pressed interrupt button") { + text2 = "Error Code 0x00000003"; + text = "The user interrupted the system's behavior."; + } else { + text2 = "Error Code 0xF" + require('crypto').createHash('md5').update(error.message).digest('hex').substring(0, 7).toUpperCase(); + text = "The system encountered a serious internal error and couldn't continue running."; + } + + clearInterval(loadInterval); + clearInterval(loadInterval2); + console.clear(); + + let out = ""; + + global.vertical = Math.round(process.stdout.rows / 2); + let horizontal = Math.round(process.stdout.columns / 2 - (1 + (text.length / 2))); + let horizontal2 = Math.round(process.stdout.columns / 2 - (1 + (text2.length / 2))); + + for (let n = 0; n < vertical; n++) { + out += "\n" + " ".repeat(process.stdout.columns); + } + + out += " ".repeat(horizontal) + text + " ".repeat(process.stdout.columns - horizontal - text.length) + "\n" + " ".repeat(horizontal2) + text2 + " ".repeat(process.stdout.columns - horizontal2 - text2.length); + + for (let n = 0; n < vertical - 2; n++) { + out += "\n" + " ".repeat(process.stdout.columns); + } + + console.log(chalk.bgRed.whiteBright(out)); + + process.stdout.cursorTo(0, 0); + if (error.message !== "User pressed interrupt button") { + process.stdout.write(error.stack); + fs.writeFileSync("lastError.txt", error.stack); + } + if (fs.existsSync("/home/strawberry/strawberry/restart")) { + fs.rmSync("/home/strawberry/strawberry/restart"); + } else { + if (error.message.endsWith("EIO")) { + console.clear(); + require('child_process').execSync("aplay /home/strawberry/strawberry/failure.wav 2> /dev/null"); + } else { + require('child_process').execSync("aplay /home/strawberry/strawberry/crash.wav 2> /dev/null"); + } + } + process.exit(); + }) + + const chalk = (await import('chalk')).default; + const os = require('os'); + const fs = require('fs'); + const si = require('systeminformation'); + + if (!fs.existsSync("/Strawberry")) fs.mkdirSync("/Strawberry"); + + process.stdin.setRawMode(true); + + function clear() { + process.stdout.cursorTo(0, vertical - 3); + process.stdout.write(chalk.bgWhite.black(" ".repeat(process.stdout.columns))); + } + + async function display() { + let horizontal = Math.round(process.stdout.columns / 2 - (1 + (message.length / 2))); + + process.stdout.cursorTo(0, vertical - 3); + process.stdout.write(chalk.bgWhite.black(" ".repeat(horizontal) + message + " ".repeat(process.stdout.columns - horizontal - message.length))); + + let disks = (await si.blockDevices()).filter((i) => { return i.type === "part"; }); + + process.stdout.cursorTo(0, process.stdout.columns - 1); + process.stdout.clearLine(undefined, undefined); + process.stdout.write(chalk.whiteBright("strawberry-boot v" + version + ", kernel-hal v" + os.release() + ", core v" + process.versions.node + " - " + process.arch + " x" + os.cpus().length + " - Partitions detected: " + disks.length + " (" + disks.map((i) => { + if (i.label === "SBSTARTUPDK") { + require('child_process').exec("mount -o ro /dev/" + i.name + " /Strawberry"); + + if (i.mount === "/Strawberry") { + global.mounted = true; + } + + return chalk.green(i.name + " " + i.label + " " + (i.mount && i.mount.trim() !== "" ? i.mount : "<unmounted>")); + } else { + if (i.mount && i.mount.trim() !== "") { + return chalk.cyan(i.name + " " + i.label + " " + (i.mount && i.mount.trim() !== "" ? i.mount : "<unmounted>")); + } else { + return chalk.red(i.name + " " + i.label + " " + (i.mount && i.mount.trim() !== "" ? i.mount : "<unmounted>")); + } + } + }).join(", ") + ")")); + process.stdout.cursorTo(0, vertical - 3); + + if (mounted) { + if (fs.existsSync("/Strawberry/System")) { + let localVersion = parseInt(fs.readFileSync("/Strawberry/System/Version").toString().trim().split("\n")[0].trim()); + if (localVersion < osVersionCode) { + error("The inserted Strawberry Startup Disk is not compatible with this device.", "Error Code 0x0000002B") + } else { + global.message = "Checking Startup Disk..."; + } + } else { + error("The inserted Strawberry Startup Disk is not compatible with this device.", "Error Code 0x0000000A") + } + + if (global.message === "Checking Startup Disk...") { + require('/Strawberry/System/Startup.js'); + } + } + } + + global.center = (text) => { + let out = ""; + global.vertical = Math.round(process.stdout.rows / 2 + 1); + let horizontal = Math.round(process.stdout.columns / 2 - (1 + (text.length / 2))); + + for (let n = 0; n < vertical; n++) { + out += "\n" + " ".repeat(process.stdout.columns); + } + + out += " ".repeat(horizontal) + text + " ".repeat(process.stdout.columns - horizontal - text.length); + + for (let n = 0; n < vertical - 2; n++) { + out += "\n" + " ".repeat(process.stdout.columns); + } + + return chalk.bgWhite.black(out); + } + + console.clear(); + console.log(center("")); + process.stdout.write(chalk.whiteBright("strawberry-boot v" + version + ", kernel-hal v" + os.release() + ", core v" + process.versions.node + " - " + process.arch + " x" + os.cpus().length + " - Partitions detected: Please wait...")); + process.stdout.cursorTo(0, 0); + + global.loadInterval = setInterval(clear, 2000); + setTimeout(() => { + global.loadInterval2 = setInterval(display, 2000); + }, 1000) + + setInterval(() => {}) +})()
\ No newline at end of file Binary files differdiff --git a/crash.wav b/crash.wav Binary files differnew file mode 100644 index 0000000..b75950c --- /dev/null +++ b/crash.wav diff --git a/failure.wav b/failure.wav Binary files differnew file mode 100644 index 0000000..47a3156 --- /dev/null +++ b/failure.wav diff --git a/node_modules/.bin/systeminformation b/node_modules/.bin/systeminformation new file mode 120000 index 0000000..681d01e --- /dev/null +++ b/node_modules/.bin/systeminformation @@ -0,0 +1 @@ +../systeminformation/lib/cli.js
\ No newline at end of file diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json new file mode 100644 index 0000000..a9879a9 --- /dev/null +++ b/node_modules/.package-lock.json @@ -0,0 +1,64 @@ +{ + "name": "strawberry-boot", + "lockfileVersion": 2, + "requires": true, + "packages": { + "node_modules/chalk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", + "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/control-sequence-introducer": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/control-sequence-introducer/-/control-sequence-introducer-0.0.2.tgz", + "integrity": "sha512-UZ8wDB3b0dEIkmUaaiDuMgNiChcBSVel5iKv+77DZ0UGrf0UnJ4Kxnp9KyrcOezDiUU4ryDyq+BODOAMcRv5+g==", + "dependencies": { + "terminal-escape-char": "*" + } + }, + "node_modules/hide-terminal-cursor": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/hide-terminal-cursor/-/hide-terminal-cursor-0.0.1.tgz", + "integrity": "sha512-HB4zGVQrBpzmexWv8+YrmIzc+Lsc/jXkMJnVY25zVoJ61Ig+pTBI6snpg7fTvjGYeQPeByp8vVCX8YHuNGX7XQ==", + "dependencies": { + "control-sequence-introducer": "0.0.2" + } + }, + "node_modules/systeminformation": { + "version": "5.11.22", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.11.22.tgz", + "integrity": "sha512-sBZJ/WBCf2vDLeMZaEyVuo+aXylOSmNHHB2cX0jHULFxSBLXHX+QUHYrCvmz+YiflKY3bsahVWX7vwuz1p1QZA==", + "os": [ + "darwin", + "linux", + "win32", + "freebsd", + "openbsd", + "netbsd", + "sunos", + "android" + ], + "bin": { + "systeminformation": "lib/cli.js" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "Buy me a coffee", + "url": "https://www.buymeacoffee.com/systeminfo" + } + }, + "node_modules/terminal-escape-char": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/terminal-escape-char/-/terminal-escape-char-0.0.4.tgz", + "integrity": "sha512-Gy0H5xwAGLKgQpid7kKRgRgrwqiVQe0b8uduRZr6mDBVH21BwUhDJEVvhBEONDt6Or+gcur0QeQDOr6rAmwnpQ==" + } + } +} diff --git a/node_modules/chalk/license b/node_modules/chalk/license new file mode 100644 index 0000000..fa7ceba --- /dev/null +++ b/node_modules/chalk/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/chalk/package.json b/node_modules/chalk/package.json new file mode 100644 index 0000000..7daddb2 --- /dev/null +++ b/node_modules/chalk/package.json @@ -0,0 +1,78 @@ +{ + "name": "chalk", + "version": "5.0.1", + "description": "Terminal string styling done right", + "license": "MIT", + "repository": "chalk/chalk", + "funding": "https://github.com/chalk/chalk?sponsor=1", + "type": "module", + "main": "./source/index.js", + "exports": "./source/index.js", + "imports": { + "#ansi-styles": "./source/vendor/ansi-styles/index.js", + "#supports-color": { + "node": "./source/vendor/supports-color/index.js", + "default": "./source/vendor/supports-color/browser.js" + } + }, + "types": "./source/index.d.ts", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "scripts": { + "test": "xo && c8 ava && tsd", + "bench": "matcha benchmark.js" + }, + "files": [ + "source", + "!source/index.test-d.ts" + ], + "keywords": [ + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "ansi", + "style", + "styles", + "tty", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "devDependencies": { + "@types/node": "^16.11.10", + "ava": "^3.15.0", + "c8": "^7.10.0", + "color-convert": "^2.0.1", + "execa": "^6.0.0", + "log-update": "^5.0.0", + "matcha": "^0.7.0", + "tsd": "^0.19.0", + "xo": "^0.47.0", + "yoctodelay": "^2.0.0" + }, + "xo": { + "rules": { + "unicorn/prefer-string-slice": "off" + } + }, + "c8": { + "reporter": [ + "text", + "lcov" + ], + "exclude": [ + "source/vendor" + ] + } +} diff --git a/node_modules/chalk/readme.md b/node_modules/chalk/readme.md new file mode 100644 index 0000000..f6adc80 --- /dev/null +++ b/node_modules/chalk/readme.md @@ -0,0 +1,317 @@ +<h1 align="center"> + <br> + <br> + <img width="320" src="media/logo.svg" alt="Chalk"> + <br> + <br> + <br> +</h1> + +> Terminal string styling done right + +[![Coverage Status](https://codecov.io/gh/chalk/chalk/branch/main/graph/badge.svg)](https://codecov.io/gh/chalk/chalk) +[![npm dependents](https://badgen.net/npm/dependents/chalk)](https://www.npmjs.com/package/chalk?activeTab=dependents) [![Downloads](https://badgen.net/npm/dt/chalk)](https://www.npmjs.com/package/chalk) +[![run on repl.it](https://repl.it/badge/github/chalk/chalk)](https://repl.it/github/chalk/chalk) +[![Support Chalk on DEV](https://badge.devprotocol.xyz/0x44d871aebF0126Bf646753E2C976Aa7e68A66c15/descriptive)](https://stakes.social/0x44d871aebF0126Bf646753E2C976Aa7e68A66c15) + +<img src="https://cdn.jsdelivr.net/gh/chalk/ansi-styles@8261697c95bf34b6c7767e2cbe9941a851d59385/screenshot.svg" width="900"> + +<br> + +--- + +<div align="center"> + <p> + <p> + <sup> + Sindre Sorhus' open source work is supported by the community on <a href="https://github.com/sponsors/sindresorhus">GitHub Sponsors</a> and <a href="https://stakes.social/0x44d871aebF0126Bf646753E2C976Aa7e68A66c15">Dev</a> + </sup> + </p> + <sup>Special thanks to:</sup> + <br> + <br> + <a href="https://standardresume.co/tech"> + <img src="https://sindresorhus.com/assets/thanks/standard-resume-logo.svg" width="160"/> + </a> + <br> + <br> + <a href="https://retool.com/?utm_campaign=sindresorhus"> + <img src="https://sindresorhus.com/assets/thanks/retool-logo.svg" width="230"/> + </a> + <br> + <br> + <a href="https://doppler.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=chalk&utm_source=github"> + <div> + <img src="https://dashboard.doppler.com/imgs/logo-long.svg" width="240" alt="Doppler"> + </div> + <b>All your environment variables, in one place</b> + <div> + <span>Stop struggling with scattered API keys, hacking together home-brewed tools,</span> + <br> + <span>and avoiding access controls. Keep your team and servers in sync with Doppler.</span> + </div> + </a> + <br> + <a href="https://strapi.io/?ref=sindresorhus"> + <div> + <img src="https://sindresorhus.com/assets/thanks/strapi-logo-white-bg.png" width="220" alt="Strapi"> + </div> + <b>Strapi is the leading open-source headless CMS.</b> + <div> + <sup>It’s 100% JavaScript, fully customizable, and developer-first.</sup> + </div> + </a> + </p> +</div> + +--- + +<br> + +## Highlights + +- Expressive API +- Highly performant +- No dependencies +- Ability to nest styles +- [256/Truecolor color support](#256-and-truecolor-color-support) +- Auto-detects color support +- Doesn't extend `String.prototype` +- Clean and focused +- Actively maintained +- [Used by ~76,000 packages](https://www.npmjs.com/browse/depended/chalk) as of October 26, 2021 + +## Install + +```sh +npm install chalk +``` + +**IMPORTANT:** Chalk 5 is ESM. If you want to use Chalk with TypeScript or a build tool, you will probably want to use Chalk 4 for now. [Read more.](https://github.com/chalk/chalk/releases/tag/v5.0.0) + +## Usage + +```js +import chalk from 'chalk'; + +console.log(chalk.blue('Hello world!')); +``` + +Chalk comes with an easy to use composable API where you just chain and nest the styles you want. + +```js +import chalk from 'chalk'; + +const log = console.log; + +// Combine styled and normal strings +log(chalk.blue('Hello') + ' World' + chalk.red('!')); + +// Compose multiple styles using the chainable API +log(chalk.blue.bgRed.bold('Hello world!')); + +// Pass in multiple arguments +log(chalk.blue('Hello', 'World!', 'Foo', 'bar', 'biz', 'baz')); + +// Nest styles +log(chalk.red('Hello', chalk.underline.bgBlue('world') + '!')); + +// Nest styles of the same type even (color, underline, background) +log(chalk.green( + 'I am a green line ' + + chalk.blue.underline.bold('with a blue substring') + + ' that becomes green again!' +)); + +// ES2015 template literal +log(` +CPU: ${chalk.red('90%')} +RAM: ${chalk.green('40%')} +DISK: ${chalk.yellow('70%')} +`); + +// Use RGB colors in terminal emulators that support it. +log(chalk.rgb(123, 45, 67).underline('Underlined reddish color')); +log(chalk.hex('#DEADED').bold('Bold gray!')); +``` + +Easily define your own themes: + +```js +import chalk from 'chalk'; + +const error = chalk.bold.red; +const warning = chalk.hex('#FFA500'); // Orange color + +console.log(error('Error!')); +console.log(warning('Warning!')); +``` + +Take advantage of console.log [string substitution](https://nodejs.org/docs/latest/api/console.html#console_console_log_data_args): + +```js +import chalk from 'chalk'; + +const name = 'Sindre'; +console.log(chalk.green('Hello %s'), name); +//=> 'Hello Sindre' +``` + +## API + +### chalk.`<style>[.<style>...](string, [string...])` + +Example: `chalk.red.bold.underline('Hello', 'world');` + +Chain [styles](#styles) and call the last one as a method with a string argument. Order doesn't matter, and later styles take precedent in case of a conflict. This simply means that `chalk.red.yellow.green` is equivalent to `chalk.green`. + +Multiple arguments will be separated by space. + +### chalk.level + +Specifies the level of color support. + +Color support is automatically detected, but you can override it by setting the `level` property. You should however only do this in your own code as it applies globally to all Chalk consumers. + +If you need to change this in a reusable module, create a new instance: + +```js +import {Chalk} from 'chalk'; + +const customChalk = new Chalk({level: 0}); +``` + +| Level | Description | +| :---: | :--- | +| `0` | All colors disabled | +| `1` | Basic color support (16 colors) | +| `2` | 256 color support | +| `3` | Truecolor support (16 million colors) | + +### supportsColor + +Detect whether the terminal [supports color](https://github.com/chalk/supports-color). Used internally and handled for you, but exposed for convenience. + +Can be overridden by the user with the flags `--color` and `--no-color`. For situations where using `--color` is not possible, use the environment variable `FORCE_COLOR=1` (level 1), `FORCE_COLOR=2` (level 2), or `FORCE_COLOR=3` (level 3) to forcefully enable color, or `FORCE_COLOR=0` to forcefully disable. The use of `FORCE_COLOR` overrides all other color support checks. + +Explicit 256/Truecolor mode can be enabled using the `--color=256` and `--color=16m` flags, respectively. + +### chalkStderr and supportsColorStderr + +`chalkStderr` contains a separate instance configured with color support detected for `stderr` stream instead of `stdout`. Override rules from `supportsColor` apply to this too. `supportsColorStderr` is exposed for convenience. + +## Styles + +### Modifiers + +- `reset` - Reset the current style. +- `bold` - Make the text bold. +- `dim` - Make the text have lower opacity. +- `italic` - Make the text italic. *(Not widely supported)* +- `underline` - Put a horizontal line below the text. *(Not widely supported)* +- `overline` - Put a horizontal line above the text. *(Not widely supported)* +- `inverse`- Invert background and foreground colors. +- `hidden` - Print the text but make it invisible. +- `strikethrough` - Puts a horizontal line through the center of the text. *(Not widely supported)* +- `visible`- Print the text only when Chalk has a color level above zero. Can be useful for things that are purely cosmetic. + +### Colors + +- `black` +- `red` +- `green` +- `yellow` +- `blue` +- `magenta` +- `cyan` +- `white` +- `blackBright` (alias: `gray`, `grey`) +- `redBright` +- `greenBright` +- `yellowBright` +- `blueBright` +- `magentaBright` +- `cyanBright` +- `whiteBright` + +### Background colors + +- `bgBlack` +- `bgRed` +- `bgGreen` +- `bgYellow` +- `bgBlue` +- `bgMagenta` +- `bgCyan` +- `bgWhite` +- `bgBlackBright` (alias: `bgGray`, `bgGrey`) +- `bgRedBright` +- `bgGreenBright` +- `bgYellowBright` +- `bgBlueBright` +- `bgMagentaBright` +- `bgCyanBright` +- `bgWhiteBright` + +## 256 and Truecolor color support + +Chalk supports 256 colors and [Truecolor](https://gist.github.com/XVilka/8346728) (16 million colors) on supported terminal apps. + +Colors are downsampled from 16 million RGB values to an ANSI color format that is supported by the terminal emulator (or by specifying `{level: n}` as a Chalk option). For example, Chalk configured to run at level 1 (basic color support) will downsample an RGB value of #FF0000 (red) to 31 (ANSI escape for red). + +Examples: + +- `chalk.hex('#DEADED').underline('Hello, world!')` +- `chalk.rgb(15, 100, 204).inverse('Hello!')` + +Background versions of these models are prefixed with `bg` and the first level of the module capitalized (e.g. `hex` for foreground colors and `bgHex` for background colors). + +- `chalk.bgHex('#DEADED').underline('Hello, world!')` +- `chalk.bgRgb(15, 100, 204).inverse('Hello!')` + +The following color models can be used: + +- [`rgb`](https://en.wikipedia.org/wiki/RGB_color_model) - Example: `chalk.rgb(255, 136, 0).bold('Orange!')` +- [`hex`](https://en.wikipedia.org/wiki/Web_colors#Hex_triplet) - Example: `chalk.hex('#FF8800').bold('Orange!')` +- [`ansi256`](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) - Example: `chalk.bgAnsi256(194)('Honeydew, more or less')` + +## Browser support + +Since Chrome 69, ANSI escape codes are natively supported in the developer console. + +## Windows + +If you're on Windows, do yourself a favor and use [Windows Terminal](https://github.com/microsoft/terminal) instead of `cmd.exe`. + +## Origin story + +[colors.js](https://github.com/Marak/colors.js) used to be the most popular string styling module, but it has serious deficiencies like extending `String.prototype` which causes all kinds of [problems](https://github.com/yeoman/yo/issues/68) and the package is unmaintained. Although there are other packages, they either do too much or not enough. Chalk is a clean and focused alternative. + +## chalk for enterprise + +Available as part of the Tidelift Subscription. + +The maintainers of chalk and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-chalk?utm_source=npm-chalk&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) + +## Related + +- [chalk-template](https://github.com/chalk/chalk-template) - [Tagged template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates) support for this module +- [chalk-cli](https://github.com/chalk/chalk-cli) - CLI for this module +- [ansi-styles](https://github.com/chalk/ansi-styles) - ANSI escape codes for styling strings in the terminal +- [supports-color](https://github.com/chalk/supports-color) - Detect whether a terminal supports color +- [strip-ansi](https://github.com/chalk/strip-ansi) - Strip ANSI escape codes +- [strip-ansi-stream](https://github.com/chalk/strip-ansi-stream) - Strip ANSI escape codes from a stream +- [has-ansi](https://github.com/chalk/has-ansi) - Check if a string has ANSI escape codes +- [ansi-regex](https://github.com/chalk/ansi-regex) - Regular expression for matching ANSI escape codes +- [wrap-ansi](https://github.com/chalk/wrap-ansi) - Wordwrap a string with ANSI escape codes +- [slice-ansi](https://github.com/chalk/slice-ansi) - Slice a string with ANSI escape codes +- [color-convert](https://github.com/qix-/color-convert) - Converts colors between different models +- [chalk-animation](https://github.com/bokub/chalk-animation) - Animate strings in the terminal +- [gradient-string](https://github.com/bokub/gradient-string) - Apply color gradients to strings +- [chalk-pipe](https://github.com/LitoMore/chalk-pipe) - Create chalk style schemes with simpler style strings +- [terminal-link](https://github.com/sindresorhus/terminal-link) - Create clickable links in the terminal + +## Maintainers + +- [Sindre Sorhus](https://github.com/sindresorhus) +- [Josh Junon](https://github.com/qix-) diff --git a/node_modules/chalk/source/index.d.ts b/node_modules/chalk/source/index.d.ts new file mode 100644 index 0000000..b2408c6 --- /dev/null +++ b/node_modules/chalk/source/index.d.ts @@ -0,0 +1,318 @@ +// TODO: Make it this when TS suports that. +// import {ColorInfo, ColorSupportLevel} from '#supports-color'; +import {ColorInfo, ColorSupportLevel} from './vendor/supports-color/index.js'; + +/** +Basic foreground colors. + +[More colors here.](https://github.com/chalk/chalk/blob/main/readme.md#256-and-truecolor-color-support) +*/ +export type ForegroundColor = + | 'black' + | 'red' + | 'green' + | 'yellow' + | 'blue' + | 'magenta' + | 'cyan' + | 'white' + | 'gray' + | 'grey' + | 'blackBright' + | 'redBright' + | 'greenBright' + | 'yellowBright' + | 'blueBright' + | 'magentaBright' + | 'cyanBright' + | 'whiteBright'; + +/** +Basic background colors. + +[More colors here.](https://github.com/chalk/chalk/blob/main/readme.md#256-and-truecolor-color-support) +*/ +export type BackgroundColor = + | 'bgBlack' + | 'bgRed' + | 'bgGreen' + | 'bgYellow' + | 'bgBlue' + | 'bgMagenta' + | 'bgCyan' + | 'bgWhite' + | 'bgGray' + | 'bgGrey' + | 'bgBlackBright' + | 'bgRedBright' + | 'bgGreenBright' + | 'bgYellowBright' + | 'bgBlueBright' + | 'bgMagentaBright' + | 'bgCyanBright' + | 'bgWhiteBright'; + +/** +Basic colors. + +[More colors here.](https://github.com/chalk/chalk/blob/main/readme.md#256-and-truecolor-color-support) +*/ +export type Color = ForegroundColor | BackgroundColor; + +export type Modifiers = + | 'reset' + | 'bold' + | 'dim' + | 'italic' + | 'underline' + | 'overline' + | 'inverse' + | 'hidden' + | 'strikethrough' + | 'visible'; + +export interface Options { + /** + Specify the color support for Chalk. + + By default, color support is automatically detected based on the environment. + + Levels: + - `0` - All colors disabled. + - `1` - Basic 16 colors support. + - `2` - ANSI 256 colors support. + - `3` - Truecolor 16 million colors support. + */ + readonly level?: ColorSupportLevel; +} + +/** +Return a new Chalk instance. +*/ +export const Chalk: new (options?: Options) => ChalkInstance; // eslint-disable-line @typescript-eslint/naming-convention + +export interface ChalkInstance { + (...text: unknown[]): string; + + /** + The color support for Chalk. + + By default, color support is automatically detected based on the environment. + + Levels: + - `0` - All colors disabled. + - `1` - Basic 16 colors support. + - `2` - ANSI 256 colors support. + - `3` - Truecolor 16 million colors support. + */ + level: ColorSupportLevel; + + /** + Use RGB values to set text color. + + @example + ``` + import chalk from 'chalk'; + + chalk.rgb(222, 173, 237); + ``` + */ + rgb: (red: number, green: number, blue: number) => this; + + /** + Use HEX value to set text color. + + @param color - Hexadecimal value representing the desired color. + + @example + ``` + import chalk from 'chalk'; + + chalk.hex('#DEADED'); + ``` + */ + hex: (color: string) => this; + + /** + Use an [8-bit unsigned number](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) to set text color. + + @example + ``` + import chalk from 'chalk'; + + chalk.ansi256(201); + ``` + */ + ansi256: (index: number) => this; + + /** + Use RGB values to set background color. + + @example + ``` + import chalk from 'chalk'; + + chalk.bgRgb(222, 173, 237); + ``` + */ + bgRgb: (red: number, green: number, blue: number) => this; + + /** + Use HEX value to set background color. + + @param color - Hexadecimal value representing the desired color. + + @example + ``` + import chalk from 'chalk'; + + chalk.bgHex('#DEADED'); + ``` + */ + bgHex: (color: string) => this; + + /** + Use a [8-bit unsigned number](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) to set background color. + + @example + ``` + import chalk from 'chalk'; + + chalk.bgAnsi256(201); + ``` + */ + bgAnsi256: (index: number) => this; + + /** + Modifier: Reset the current style. + */ + readonly reset: this; + + /** + Modifier: Make the text bold. + */ + readonly bold: this; + + /** + Modifier: Make the text have lower opacity. + */ + readonly dim: this; + + /** + Modifier: Make the text italic. *(Not widely supported)* + */ + readonly italic: this; + + /** + Modifier: Put a horizontal line below the text. *(Not widely supported)* + */ + readonly underline: this; + + /** + Modifier: Put a horizontal line above the text. *(Not widely supported)* + */ + readonly overline: this; + + /** + Modifier: Invert background and foreground colors. + */ + readonly inverse: this; + + /** + Modifier: Print the text but make it invisible. + */ + readonly hidden: this; + + /** + Modifier: Puts a horizontal line through the center of the text. *(Not widely supported)* + */ + readonly strikethrough: this; + + /** + Modifier: Print the text only when Chalk has a color level above zero. + + Can be useful for things that are purely cosmetic. + */ + readonly visible: this; + + readonly black: this; + readonly red: this; + readonly green: this; + readonly yellow: this; + readonly blue: this; + readonly magenta: this; + readonly cyan: this; + readonly white: this; + + /* + Alias for `blackBright`. + */ + readonly gray: this; + + /* + Alias for `blackBright`. + */ + readonly grey: this; + + readonly blackBright: this; + readonly redBright: this; + readonly greenBright: this; + readonly yellowBright: this; + readonly blueBright: this; + readonly magentaBright: this; + readonly cyanBright: this; + readonly whiteBright: this; + + readonly bgBlack: this; + readonly bgRed: this; + readonly bgGreen: this; + readonly bgYellow: this; + readonly bgBlue: this; + readonly bgMagenta: this; + readonly bgCyan: this; + readonly bgWhite: this; + + /* + Alias for `bgBlackBright`. + */ + readonly bgGray: this; + + /* + Alias for `bgBlackBright`. + */ + readonly bgGrey: this; + + readonly bgBlackBright: this; + readonly bgRedBright: this; + readonly bgGreenBright: this; + readonly bgYellowBright: this; + readonly bgBlueBright: this; + readonly bgMagentaBright: this; + readonly bgCyanBright: this; + readonly bgWhiteBright: this; +} + +/** +Main Chalk object that allows to chain styles together. + +Call the last one as a method with a string argument. + +Order doesn't matter, and later styles take precedent in case of a conflict. + +This simply means that `chalk.red.yellow.green` is equivalent to `chalk.green`. +*/ +declare const chalk: ChalkInstance; + +export const supportsColor: ColorInfo; + +export const chalkStderr: typeof chalk; +export const supportsColorStderr: typeof supportsColor; + +export { + ColorInfo, + ColorSupport, + ColorSupportLevel, +// } from '#supports-color'; +} from './vendor/supports-color/index.js'; + +export default chalk; diff --git a/node_modules/chalk/source/index.js b/node_modules/chalk/source/index.js new file mode 100644 index 0000000..302024b --- /dev/null +++ b/node_modules/chalk/source/index.js @@ -0,0 +1,212 @@ +import ansiStyles from '#ansi-styles'; +import supportsColor from '#supports-color'; +import { // eslint-disable-line import/order + stringReplaceAll, + stringEncaseCRLFWithFirstIndex, +} from './utilities.js'; + +const {stdout: stdoutColor, stderr: stderrColor} = supportsColor; + +const GENERATOR = Symbol('GENERATOR'); +const STYLER = Symbol('STYLER'); +const IS_EMPTY = Symbol('IS_EMPTY'); + +// `supportsColor.level` → `ansiStyles.color[name]` mapping +const levelMapping = [ + 'ansi', + 'ansi', + 'ansi256', + 'ansi16m', +]; + +const styles = Object.create(null); + +const applyOptions = (object, options = {}) => { + if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) { + throw new Error('The `level` option should be an integer from 0 to 3'); + } + + // Detect level if not set manually + const colorLevel = stdoutColor ? stdoutColor.level : 0; + object.level = options.level === undefined ? colorLevel : options.level; +}; + +export class Chalk { + constructor(options) { + // eslint-disable-next-line no-constructor-return + return chalkFactory(options); + } +} + +const chalkFactory = options => { + const chalk = (...strings) => strings.join(' '); + applyOptions(chalk, options); + + Object.setPrototypeOf(chalk, createChalk.prototype); + + return chalk; +}; + +function createChalk(options) { + return chalkFactory(options); +} + +Object.setPrototypeOf(createChalk.prototype, Function.prototype); + +for (const [styleName, style] of Object.entries(ansiStyles)) { + styles[styleName] = { + get() { + const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]); + Object.defineProperty(this, styleName, {value: builder}); + return builder; + }, + }; +} + +styles.visible = { + get() { + const builder = createBuilder(this, this[STYLER], true); + Object.defineProperty(this, 'visible', {value: builder}); + return builder; + }, +}; + +const getModelAnsi = (model, level, type, ...arguments_) => { + if (model === 'rgb') { + if (level === 'ansi16m') { + return ansiStyles[type].ansi16m(...arguments_); + } + + if (level === 'ansi256') { + return ansiStyles[type].ansi256(ansiStyles.rgbToAnsi256(...arguments_)); + } + + return ansiStyles[type].ansi(ansiStyles.rgbToAnsi(...arguments_)); + } + + if (model === 'hex') { + return getModelAnsi('rgb', level, type, ...ansiStyles.hexToRgb(...arguments_)); + } + + return ansiStyles[type][model](...arguments_); +}; + +const usedModels = ['rgb', 'hex', 'ansi256']; + +for (const model of usedModels) { + styles[model] = { + get() { + const {level} = this; + return function (...arguments_) { + const styler = createStyler(getModelAnsi(model, levelMapping[level], 'color', ...arguments_), ansiStyles.color.close, this[STYLER]); + return createBuilder(this, styler, this[IS_EMPTY]); + }; + }, + }; + + const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1); + styles[bgModel] = { + get() { + const {level} = this; + return function (...arguments_) { + const styler = createStyler(getModelAnsi(model, levelMapping[level], 'bgColor', ...arguments_), ansiStyles.bgColor.close, this[STYLER]); + return createBuilder(this, styler, this[IS_EMPTY]); + }; + }, + }; +} + +const proto = Object.defineProperties(() => {}, { + ...styles, + level: { + enumerable: true, + get() { + return this[GENERATOR].level; + }, + set(level) { + this[GENERATOR].level = level; + }, + }, +}); + +const createStyler = (open, close, parent) => { + let openAll; + let closeAll; + if (parent === undefined) { + openAll = open; + closeAll = close; + } else { + openAll = parent.openAll + open; + closeAll = close + parent.closeAll; + } + + return { + open, + close, + openAll, + closeAll, + parent, + }; +}; + +const createBuilder = (self, _styler, _isEmpty) => { + // Single argument is hot path, implicit coercion is faster than anything + // eslint-disable-next-line no-implicit-coercion + const builder = (...arguments_) => applyStyle(builder, (arguments_.length === 1) ? ('' + arguments_[0]) : arguments_.join(' ')); + + // We alter the prototype because we must return a function, but there is + // no way to create a function with a different prototype + Object.setPrototypeOf(builder, proto); + + builder[GENERATOR] = self; + builder[STYLER] = _styler; + builder[IS_EMPTY] = _isEmpty; + + return builder; +}; + +const applyStyle = (self, string) => { + if (self.level <= 0 || !string) { + return self[IS_EMPTY] ? '' : string; + } + + let styler = self[STYLER]; + + if (styler === undefined) { + return string; + } + + const {openAll, closeAll} = styler; + if (string.includes('\u001B')) { + while (styler !== undefined) { + // Replace any instances already present with a re-opening code + // otherwise only the part of the string until said closing code + // will be colored, and the rest will simply be 'plain'. + string = stringReplaceAll(string, styler.close, styler.open); + + styler = styler.parent; + } + } + + // We can move both next actions out of loop, because remaining actions in loop won't have + // any/visible effect on parts we add here. Close the styling before a linebreak and reopen + // after next line to fix a bleed issue on macOS: https://github.com/chalk/chalk/pull/92 + const lfIndex = string.indexOf('\n'); + if (lfIndex !== -1) { + string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex); + } + + return openAll + string + closeAll; +}; + +Object.defineProperties(createChalk.prototype, styles); + +const chalk = createChalk(); +export const chalkStderr = createChalk({level: stderrColor ? stderrColor.level : 0}); + +export { + stdoutColor as supportsColor, + stderrColor as supportsColorStderr, +}; + +export default chalk; diff --git a/node_modules/chalk/source/utilities.js b/node_modules/chalk/source/utilities.js new file mode 100644 index 0000000..5d49dad --- /dev/null +++ b/node_modules/chalk/source/utilities.js @@ -0,0 +1,33 @@ +// TODO: When targeting Node.js 16, use `String.prototype.replaceAll`. +export function stringReplaceAll(string, substring, replacer) { + let index = string.indexOf(substring); + if (index === -1) { + return string; + } + + const substringLength = substring.length; + let endIndex = 0; + let returnValue = ''; + do { + returnValue += string.substr(endIndex, index - endIndex) + substring + replacer; + endIndex = index + substringLength; + index = string.indexOf(substring, endIndex); + } while (index !== -1); + + returnValue += string.slice(endIndex); + return returnValue; +} + +export function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) { + let endIndex = 0; + let returnValue = ''; + do { + const gotCR = string[index - 1] === '\r'; + returnValue += string.substr(endIndex, (gotCR ? index - 1 : index) - endIndex) + prefix + (gotCR ? '\r\n' : '\n') + postfix; + endIndex = index + 1; + index = string.indexOf('\n', endIndex); + } while (index !== -1); + + returnValue += string.slice(endIndex); + return returnValue; +} diff --git a/node_modules/chalk/source/vendor/ansi-styles/index.d.ts b/node_modules/chalk/source/vendor/ansi-styles/index.d.ts new file mode 100644 index 0000000..7518d2a --- /dev/null +++ b/node_modules/chalk/source/vendor/ansi-styles/index.d.ts @@ -0,0 +1,190 @@ +export interface CSPair { // eslint-disable-line @typescript-eslint/naming-convention + /** + The ANSI terminal control sequence for starting this style. + */ + readonly open: string; + + /** + The ANSI terminal control sequence for ending this style. + */ + readonly close: string; +} + +export interface ColorBase { + /** + The ANSI terminal control sequence for ending this color. + */ + readonly close: string; + + ansi(code: number): string; + + ansi256(code: number): string; + + ansi16m(red: number, green: number, blue: number): string; +} + +export interface Modifier { + /** + Resets the current color chain. + */ + readonly reset: CSPair; + + /** + Make text bold. + */ + readonly bold: CSPair; + + /** + Emitting only a small amount of light. + */ + readonly dim: CSPair; + + /** + Make text italic. (Not widely supported) + */ + readonly italic: CSPair; + + /** + Make text underline. (Not widely supported) + */ + readonly underline: CSPair; + + /** + Make text overline. + + Supported on VTE-based terminals, the GNOME terminal, mintty, and Git Bash. + */ + readonly overline: CSPair; + + /** + Inverse background and foreground colors. + */ + readonly inverse: CSPair; + + /** + Prints the text, but makes it invisible. + */ + readonly hidden: CSPair; + + /** + Puts a horizontal line through the center of the text. (Not widely supported) + */ + readonly strikethrough: CSPair; +} + +export interface ForegroundColor { + readonly black: CSPair; + readonly red: CSPair; + readonly green: CSPair; + readonly yellow: CSPair; + readonly blue: CSPair; + readonly cyan: CSPair; + readonly magenta: CSPair; + readonly white: CSPair; + + /** + Alias for `blackBright`. + */ + readonly gray: CSPair; + + /** + Alias for `blackBright`. + */ + readonly grey: CSPair; + + readonly blackBright: CSPair; + readonly redBright: CSPair; + readonly greenBright: CSPair; + readonly yellowBright: CSPair; + readonly blueBright: CSPair; + readonly cyanBright: CSPair; + readonly magentaBright: CSPair; + readonly whiteBright: CSPair; +} + +export interface BackgroundColor { + readonly bgBlack: CSPair; + readonly bgRed: CSPair; + readonly bgGreen: CSPair; + readonly bgYellow: CSPair; + readonly bgBlue: CSPair; + readonly bgCyan: CSPair; + readonly bgMagenta: CSPair; + readonly bgWhite: CSPair; + + /** + Alias for `bgBlackBright`. + */ + readonly bgGray: CSPair; + + /** + Alias for `bgBlackBright`. + */ + readonly bgGrey: CSPair; + + readonly bgBlackBright: CSPair; + readonly bgRedBright: CSPair; + readonly bgGreenBright: CSPair; + readonly bgYellowBright: CSPair; + readonly bgBlueBright: CSPair; + readonly bgCyanBright: CSPair; + readonly bgMagentaBright: CSPair; + readonly bgWhiteBright: CSPair; +} + +export interface ConvertColor { + /** + Convert from the RGB color space to the ANSI 256 color space. + + @param red - (`0...255`) + @param green - (`0...255`) + @param blue - (`0...255`) + */ + rgbToAnsi256(red: number, green: number, blue: number): number; + + /** + Convert from the RGB HEX color space to the RGB color space. + + @param hex - A hexadecimal string containing RGB data. + */ + hexToRgb(hex: string): [red: number, green: number, blue: number]; + + /** + Convert from the RGB HEX color space to the ANSI 256 color space. + + @param hex - A hexadecimal string containing RGB data. + */ + hexToAnsi256(hex: string): number; + + /** + Convert from the ANSI 256 color space to the ANSI 16 color space. + + @param code - A number representing the ANSI 256 color. + */ + ansi256ToAnsi(code: number): number; + + /** + Convert from the RGB color space to the ANSI 16 color space. + + @param red - (`0...255`) + @param green - (`0...255`) + @param blue - (`0...255`) + */ + rgbToAnsi(red: number, green: number, blue: number): number; + + /** + Convert from the RGB HEX color space to the ANSI 16 color space. + + @param hex - A hexadecimal string containing RGB data. + */ + hexToAnsi(hex: string): number; +} + +declare const ansiStyles: { + readonly modifier: Modifier; + readonly color: ColorBase & ForegroundColor; + readonly bgColor: ColorBase & BackgroundColor; + readonly codes: ReadonlyMap<number, number>; +} & ForegroundColor & BackgroundColor & Modifier & ConvertColor; + +export default ansiStyles; diff --git a/node_modules/chalk/source/vendor/ansi-styles/index.js b/node_modules/chalk/source/vendor/ansi-styles/index.js new file mode 100644 index 0000000..5746537 --- /dev/null +++ b/node_modules/chalk/source/vendor/ansi-styles/index.js @@ -0,0 +1,219 @@ +const ANSI_BACKGROUND_OFFSET = 10; + +const wrapAnsi16 = (offset = 0) => code => `\u001B[${code + offset}m`; + +const wrapAnsi256 = (offset = 0) => code => `\u001B[${38 + offset};5;${code}m`; + +const wrapAnsi16m = (offset = 0) => (red, green, blue) => `\u001B[${38 + offset};2;${red};${green};${blue}m`; + +function assembleStyles() { + const codes = new Map(); + const styles = { + modifier: { + reset: [0, 0], + // 21 isn't widely supported and 22 does the same thing + bold: [1, 22], + dim: [2, 22], + italic: [3, 23], + underline: [4, 24], + overline: [53, 55], + inverse: [7, 27], + hidden: [8, 28], + strikethrough: [9, 29], + }, + color: { + black: [30, 39], + red: [31, 39], + green: [32, 39], + yellow: [33, 39], + blue: [34, 39], + magenta: [35, 39], + cyan: [36, 39], + white: [37, 39], + + // Bright color + blackBright: [90, 39], + redBright: [91, 39], + greenBright: [92, 39], + yellowBright: [93, 39], + blueBright: [94, 39], + magentaBright: [95, 39], + cyanBright: [96, 39], + whiteBright: [97, 39], + }, + bgColor: { + bgBlack: [40, 49], + bgRed: [41, 49], + bgGreen: [42, 49], + bgYellow: [43, 49], + bgBlue: [44, 49], + bgMagenta: [45, 49], + bgCyan: [46, 49], + bgWhite: [47, 49], + + // Bright color + bgBlackBright: [100, 49], + bgRedBright: [101, 49], + bgGreenBright: [102, 49], + bgYellowBright: [103, 49], + bgBlueBright: [104, 49], + bgMagentaBright: [105, 49], + bgCyanBright: [106, 49], + bgWhiteBright: [107, 49], + }, + }; + + // Alias bright black as gray (and grey) + styles.color.gray = styles.color.blackBright; + styles.bgColor.bgGray = styles.bgColor.bgBlackBright; + styles.color.grey = styles.color.blackBright; + styles.bgColor.bgGrey = styles.bgColor.bgBlackBright; + + for (const [groupName, group] of Object.entries(styles)) { + for (const [styleName, style] of Object.entries(group)) { + styles[styleName] = { + open: `\u001B[${style[0]}m`, + close: `\u001B[${style[1]}m`, + }; + + group[styleName] = styles[styleName]; + + codes.set(style[0], style[1]); + } + + Object.defineProperty(styles, groupName, { + value: group, + enumerable: false, + }); + } + + Object.defineProperty(styles, 'codes', { + value: codes, + enumerable: false, + }); + + styles.color.close = '\u001B[39m'; + styles.bgColor.close = '\u001B[49m'; + + styles.color.ansi = wrapAnsi16(); + styles.color.ansi256 = wrapAnsi256(); + styles.color.ansi16m = wrapAnsi16m(); + styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET); + styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET); + styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET); + + // From https://github.com/Qix-/color-convert/blob/3f0e0d4e92e235796ccb17f6e85c72094a651f49/conversions.js + Object.defineProperties(styles, { + rgbToAnsi256: { + value: (red, green, blue) => { + // We use the extended greyscale palette here, with the exception of + // black and white. normal palette only has 4 greyscale shades. + if (red === green && green === blue) { + if (red < 8) { + return 16; + } + + if (red > 248) { + return 231; + } + + return Math.round(((red - 8) / 247) * 24) + 232; + } + + return 16 + + (36 * Math.round(red / 255 * 5)) + + (6 * Math.round(green / 255 * 5)) + + Math.round(blue / 255 * 5); + }, + enumerable: false, + }, + hexToRgb: { + value: hex => { + const matches = /(?<colorString>[a-f\d]{6}|[a-f\d]{3})/i.exec(hex.toString(16)); + if (!matches) { + return [0, 0, 0]; + } + + let {colorString} = matches.groups; + + if (colorString.length === 3) { + colorString = [...colorString].map(character => character + character).join(''); + } + + const integer = Number.parseInt(colorString, 16); + + return [ + /* eslint-disable no-bitwise */ + (integer >> 16) & 0xFF, + (integer >> 8) & 0xFF, + integer & 0xFF, + /* eslint-enable no-bitwise */ + ]; + }, + enumerable: false, + }, + hexToAnsi256: { + value: hex => styles.rgbToAnsi256(...styles.hexToRgb(hex)), + enumerable: false, + }, + ansi256ToAnsi: { + value: code => { + if (code < 8) { + return 30 + code; + } + + if (code < 16) { + return 90 + (code - 8); + } + + let red; + let green; + let blue; + + if (code >= 232) { + red = (((code - 232) * 10) + 8) / 255; + green = red; + blue = red; + } else { + code -= 16; + + const remainder = code % 36; + + red = Math.floor(code / 36) / 5; + green = Math.floor(remainder / 6) / 5; + blue = (remainder % 6) / 5; + } + + const value = Math.max(red, green, blue) * 2; + + if (value === 0) { + return 30; + } + + // eslint-disable-next-line no-bitwise + let result = 30 + ((Math.round(blue) << 2) | (Math.round(green) << 1) | Math.round(red)); + + if (value === 2) { + result += 60; + } + + return result; + }, + enumerable: false, + }, + rgbToAnsi: { + value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)), + enumerable: false, + }, + hexToAnsi: { + value: hex => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)), + enumerable: false, + }, + }); + + return styles; +} + +const ansiStyles = assembleStyles(); + +export default ansiStyles; diff --git a/node_modules/chalk/source/vendor/supports-color/browser.d.ts b/node_modules/chalk/source/vendor/supports-color/browser.d.ts new file mode 100644 index 0000000..0e8a9cc --- /dev/null +++ b/node_modules/chalk/source/vendor/supports-color/browser.d.ts @@ -0,0 +1 @@ +export {default} from './index.js'; diff --git a/node_modules/chalk/source/vendor/supports-color/browser.js b/node_modules/chalk/source/vendor/supports-color/browser.js new file mode 100644 index 0000000..f4e9acd --- /dev/null +++ b/node_modules/chalk/source/vendor/supports-color/browser.js @@ -0,0 +1,17 @@ +/* eslint-env browser */ + +const isBlinkBasedBrowser = /\b(Chrome|Chromium)\//.test(navigator.userAgent); + +const colorSupport = isBlinkBasedBrowser ? { + level: 1, + hasBasic: true, + has256: false, + has16m: false, +} : false; + +const supportsColor = { + stdout: colorSupport, + stderr: colorSupport, +}; + +export default supportsColor; diff --git a/node_modules/chalk/source/vendor/supports-color/index.d.ts b/node_modules/chalk/source/vendor/supports-color/index.d.ts new file mode 100644 index 0000000..98e3618 --- /dev/null +++ b/node_modules/chalk/source/vendor/supports-color/index.d.ts @@ -0,0 +1,55 @@ +import {WriteStream} from 'node:tty'; + +export interface Options { + /** + Whether `process.argv` should be sniffed for `--color` and `--no-color` flags. + + @default true + */ + readonly sniffFlags?: boolean; +} + +/** +Levels: +- `0` - All colors disabled. +- `1` - Basic 16 colors support. +- `2` - ANSI 256 colors support. +- `3` - Truecolor 16 million colors support. +*/ +export type ColorSupportLevel = 0 | 1 | 2 | 3; + +/** +Detect whether the terminal supports color. +*/ +export interface ColorSupport { + /** + The color level. + */ + level: ColorSupportLevel; + + /** + Whether basic 16 colors are supported. + */ + hasBasic: boolean; + + /** + Whether ANSI 256 colors are supported. + */ + has256: boolean; + + /** + Whether Truecolor 16 million colors are supported. + */ + has16m: boolean; +} + +export type ColorInfo = ColorSupport | false; + +export function createSupportsColor(stream: WriteStream, options?: Options): ColorInfo; + +declare const supportsColor: { + stdout: ColorInfo; + stderr: ColorInfo; +}; + +export default supportsColor; diff --git a/node_modules/chalk/source/vendor/supports-color/index.js b/node_modules/chalk/source/vendor/supports-color/index.js new file mode 100644 index 0000000..55f813c --- /dev/null +++ b/node_modules/chalk/source/vendor/supports-color/index.js @@ -0,0 +1,169 @@ +import process from 'node:process'; +import os from 'node:os'; +import tty from 'node:tty'; + +// From: https://github.com/sindresorhus/has-flag/blob/main/index.js +function hasFlag(flag, argv = process.argv) { + const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--'); + const position = argv.indexOf(prefix + flag); + const terminatorPosition = argv.indexOf('--'); + return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition); +} + +const {env} = process; + +let flagForceColor; +if ( + hasFlag('no-color') + || hasFlag('no-colors') + || hasFlag('color=false') + || hasFlag('color=never') +) { + flagForceColor = 0; +} else if ( + hasFlag('color') + || hasFlag('colors') + || hasFlag('color=true') + || hasFlag('color=always') +) { + flagForceColor = 1; +} + +function envForceColor() { + if ('FORCE_COLOR' in env) { + if (env.FORCE_COLOR === 'true') { + return 1; + } + + if (env.FORCE_COLOR === 'false') { + return 0; + } + + return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3); + } +} + +function translateLevel(level) { + if (level === 0) { + return false; + } + + return { + level, + hasBasic: true, + has256: level >= 2, + has16m: level >= 3, + }; +} + +function _supportsColor(haveStream, {streamIsTTY, sniffFlags = true} = {}) { + const noFlagForceColor = envForceColor(); + if (noFlagForceColor !== undefined) { + flagForceColor = noFlagForceColor; + } + + const forceColor = sniffFlags ? flagForceColor : noFlagForceColor; + + if (forceColor === 0) { + return 0; + } + + if (sniffFlags) { + if (hasFlag('color=16m') + || hasFlag('color=full') + || hasFlag('color=truecolor')) { + return 3; + } + + if (hasFlag('color=256')) { + return 2; + } + } + + if (haveStream && !streamIsTTY && forceColor === undefined) { + return 0; + } + + const min = forceColor || 0; + + if (env.TERM === 'dumb') { + return min; + } + + if (process.platform === 'win32') { + // Windows 10 build 10586 is the first Windows release that supports 256 colors. + // Windows 10 build 14931 is the first release that supports 16m/TrueColor. + const osRelease = os.release().split('.'); + if ( + Number(osRelease[0]) >= 10 + && Number(osRelease[2]) >= 10_586 + ) { + return Number(osRelease[2]) >= 14_931 ? 3 : 2; + } + + return 1; + } + + if ('CI' in env) { + if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI', 'GITHUB_ACTIONS', 'BUILDKITE', 'DRONE'].some(sign => sign in env) || env.CI_NAME === 'codeship') { + return 1; + } + + return min; + } + + if ('TEAMCITY_VERSION' in env) { + return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; + } + + // Check for Azure DevOps pipelines + if ('TF_BUILD' in env && 'AGENT_NAME' in env) { + return 1; + } + + if (env.COLORTERM === 'truecolor') { + return 3; + } + + if ('TERM_PROGRAM' in env) { + const version = Number.parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10); + + switch (env.TERM_PROGRAM) { + case 'iTerm.app': + return version >= 3 ? 3 : 2; + case 'Apple_Terminal': + return 2; + // No default + } + } + + if (/-256(color)?$/i.test(env.TERM)) { + return 2; + } + + if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { + return 1; + } + + if ('COLORTERM' in env) { + return 1; + } + + return min; +} + +export function createSupportsColor(stream, options = {}) { + const level = _supportsColor(stream, { + streamIsTTY: stream && stream.isTTY, + ...options, + }); + + return translateLevel(level); +} + +const supportsColor = { + stdout: createSupportsColor({isTTY: tty.isatty(1)}), + stderr: createSupportsColor({isTTY: tty.isatty(2)}), +}; + +export default supportsColor; diff --git a/node_modules/control-sequence-introducer/.npmignore b/node_modules/control-sequence-introducer/.npmignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/node_modules/control-sequence-introducer/.npmignore @@ -0,0 +1 @@ +node_modules
\ No newline at end of file diff --git a/node_modules/control-sequence-introducer/.travis.yml b/node_modules/control-sequence-introducer/.travis.yml new file mode 100644 index 0000000..ab2fee2 --- /dev/null +++ b/node_modules/control-sequence-introducer/.travis.yml @@ -0,0 +1,6 @@ +language: node_js +node_js: + - '5' + - '4' + - '0.12' + - '0.10' diff --git a/node_modules/control-sequence-introducer/index.js b/node_modules/control-sequence-introducer/index.js new file mode 100644 index 0000000..8fcb4b9 --- /dev/null +++ b/node_modules/control-sequence-introducer/index.js @@ -0,0 +1,6 @@ +"use strict"; +var escapeCharacter = require("terminal-escape-char"); + +var controlSequenceIntroducer = escapeCharacter + "["; + +module.exports = controlSequenceIntroducer diff --git a/node_modules/control-sequence-introducer/package.json b/node_modules/control-sequence-introducer/package.json new file mode 100644 index 0000000..5fbe56e --- /dev/null +++ b/node_modules/control-sequence-introducer/package.json @@ -0,0 +1,29 @@ +{ + "name": "control-sequence-introducer", + "version": "0.0.2", + "description": "Get Control Sequence Introducer for [ANSI Escape Codes](https://en.wikipedia.org/wiki/ANSI_escape_code)", + "main": "index.js", + "scripts": { + "test": "mocha test.js" + }, + "repository": "joegesualdo/control-sequence-introducer", + "keywords": [ + "ansi", + "escape", + "codes", + "control", + "sequence", + "introducer" + ], + "author": { + "name": "Joe Gesualdo" + }, + "license": "MIT", + "devDependencies": { + "mocha": "*", + "chai": "*" + }, + "dependencies": { + "terminal-escape-char": "*" + } +} diff --git a/node_modules/control-sequence-introducer/readme.md b/node_modules/control-sequence-introducer/readme.md new file mode 100644 index 0000000..4240f11 --- /dev/null +++ b/node_modules/control-sequence-introducer/readme.md @@ -0,0 +1,21 @@ +## Control Sequence Introducer [![Build Status](https://travis-ci.org/joegesualdo/control-sequence-introducer.svg?branch=master)](https://travis-ci.org/joegesualdo/control-sequence-introducer) +> Get Control Sequence Introducer for [ANSI Escape Codes](https://en.wikipedia.org/wiki/ANSI_escape_code) + +## Install +``` +$ npm install --save control-sequence-introducer +``` + +## Usage +```javascript +var csi = require("control-sequence-introducer") + +console.log(csi) +``` + +## Test +``` +$ npm test +``` +## License +MIT © [Joe Gesualdo]() diff --git a/node_modules/control-sequence-introducer/test.js b/node_modules/control-sequence-introducer/test.js new file mode 100644 index 0000000..767b33e --- /dev/null +++ b/node_modules/control-sequence-introducer/test.js @@ -0,0 +1,9 @@ +var expect = require("chai").expect +var escapeCharacter = require("terminal-escape-char"); +var controlSequenceIntroducer = require("./index") + +describe("controlSequenceIntroducer", function(){ + it("is correct", function(){ + expect(controlSequenceIntroducer).to.equal(escapeCharacter + "[") + }) +}) diff --git a/node_modules/hide-terminal-cursor/.npmignore b/node_modules/hide-terminal-cursor/.npmignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/node_modules/hide-terminal-cursor/.npmignore @@ -0,0 +1 @@ +node_modules
\ No newline at end of file diff --git a/node_modules/hide-terminal-cursor/index.js b/node_modules/hide-terminal-cursor/index.js new file mode 100644 index 0000000..dd37dcb --- /dev/null +++ b/node_modules/hide-terminal-cursor/index.js @@ -0,0 +1,13 @@ +"use strict"; + +var csi = require("control-sequence-introducer") + +function hideCursorString() { + return csi + "?25l" +} + +function hideCursor() { + process.stdout.write(hideCursorString()) +} + +module.exports = hideCursor; diff --git a/node_modules/hide-terminal-cursor/package.json b/node_modules/hide-terminal-cursor/package.json new file mode 100644 index 0000000..2d95100 --- /dev/null +++ b/node_modules/hide-terminal-cursor/package.json @@ -0,0 +1,27 @@ +{ + "name": "hide-terminal-cursor", + "version": "0.0.1", + "description": "Hide the terminal cursor.", + "main": "index.js", + "scripts": { + "test": "mocha test.js" + }, + "repository": "joegesualdo/hide-terminal-cursor", + "keywords": [ + "cursor", + "terminal", + "hide", + "display" + ], + "author": { + "name": "Joe Gesualdo" + }, + "license": "MIT", + "devDependencies": { + "mocha": "*", + "chai": "*" + }, + "dependencies": { + "control-sequence-introducer": "0.0.2" + } +} diff --git a/node_modules/hide-terminal-cursor/readme.md b/node_modules/hide-terminal-cursor/readme.md new file mode 100644 index 0000000..a8f1396 --- /dev/null +++ b/node_modules/hide-terminal-cursor/readme.md @@ -0,0 +1,20 @@ +## hide-terminal-cursor +> Hide the terminal cursor. + +## Install +``` +$ npm install --save hide-terminal-cursor +``` + +## Usage +```javascript +var hideCursor = require("hide-terminal-cursor") + +hideCursor(); +``` + +## Related +- [show-terminal-cursor](https://github.com/joegesualdo/show-terminal-cursor) - Shows the terminal cursor. + +## License +MIT © [Joe Gesualdo]() diff --git a/node_modules/systeminformation/LICENSE b/node_modules/systeminformation/LICENSE new file mode 100644 index 0000000..72cc9fe --- /dev/null +++ b/node_modules/systeminformation/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2014-2021 Sebastian Hildebrandt + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/systeminformation/README.md b/node_modules/systeminformation/README.md new file mode 100644 index 0000000..48b3346 --- /dev/null +++ b/node_modules/systeminformation/README.md @@ -0,0 +1,1087 @@ +<p align="center"> + <a href="https://systeminformation.io/"> + <img src="https://systeminformation.io/assets/logo_inv.png" alt="systeminformation logo" width="102" height="72"> + </a> +</p> + +<h3 align="center">systeminformation</h3> + +<p align="center"> + System and OS information library for node.js + <br> + <a href="https://systeminformation.io/"><strong>Explore Systeminformation docs »</strong></a> + <br> + <br> + <a href="https://github.com/sebhildebrandt/systeminformation/issues/new?template=bug_report.md">Report bug</a> + · + <a href="https://github.com/sebhildebrandt/systeminformation/issues/new?template=feature_request.md&labels=feature">Request feature</a> + · + <a href="https://github.com/sebhildebrandt/systeminformation/blob/master/CHANGELOG.md">Changelog</a> +</p> + + [![NPM Version][npm-image]][npm-url] + [![NPM Downloads][downloads-image]][downloads-url] + [![Git Issues][issues-img]][issues-url] + [![Closed Issues][closed-issues-img]][closed-issues-url] + <img src="docs/assets/no-dependencies.svg" alt="no dependencies"> + [![Code Quality: Javascript][lgtm-badge]][lgtm-badge-url] + [![Total alerts][lgtm-alerts]][lgtm-alerts-url] + [![Caretaker][caretaker-image]][caretaker-url] + [![Sponsoring][sponsor-badge]][sponsor-url] + [![MIT license][license-img]][license-url] + +This is amazing. Started as a small project just for myself, it now has > 15,000 lines of code, > 500 versions published, > 4 mio downloads per month, > 90 mio downloads overall. #1 NPM ranking for backend packages. Thank you to all who contributed to this project! + +## New Version 5.0 + +The new Version 5 is here - I spend several weeks finalizing this new version. Any support is highly appreciated - [Buy me a coffee](https://www.buymeacoffee.com/systeminfo) + +This next major version release 5.0 comes with new functionality and several improvements and changes (some of them are breaking changes!): + +- added audio: get detailed audio device information +- added bluetooth: get detailed bluetooth device information +- added dockerImages, dockerVolumes: get detailed information about docker images and volumes +- added printer: get information from detected printers +- added usb: get detailed usb controller and device information +- added wifi interfaces ans connections: extended wifi information +- better uuid function to get unique hardware and OS UUIDs +- better/extended cpu info detection +- better/extended system info detection +- Apple Silicon M1 support +- better Raspberry-PI detection +- systeminformation website updated and extendet with full documentation and examples [systeminformation.io][systeminformation-url] +- lot of minor improvements + +Breaking Changes in version 5: you will see several breaking changes for the sake of a more consistent API interface and to be future proof. Read the [detailed version 5 changes][changes5-url]. + +I did a lot of testing on different platforms and machines but of course there might be some issues that I am not aware of. I would be happy if you inform me when you discover any issues. Issues can be [opened here][new-issue]. + +## Quick Start + +Lightweight collection of 50+ functions to retrieve detailed hardware, system and OS information. + +- simple to use +- get detailed information about system, cpu, baseboard, battery, memory, disks/filesystem, network, docker, software, services and processes +- supports Linux, macOS, partial Windows, FreeBSD, OpenBSD, NetBSD, SunOS and Android support +- no npm dependencies + +**Attention**: this is a `node.js` library. It is supposed to be used as a backend/server-side library and will definitely not work within a browser. + +### Installation + +```bash +npm install systeminformation --save +``` + +or simpler + +```bash +npm install systeminformation +``` + +#### Still need Version 4? + +If you need version 4 (for compatibility reasons), you can install version 4 (latest release) like this + +```bash +npm install systeminformation@4 —save +``` + +or simpler + +```bash +npm install systeminformation@4 +``` + +### Usage + +All functions (except `version` and `time`) are implemented as asynchronous functions. Here a small example how to use them: + +```js +const si = require('systeminformation'); + +// promises style - new since version 3 +si.cpu() + .then(data => console.log(data)) + .catch(error => console.error(error)); +``` + +## News and Changes + +### Latest Activity + +(last 7 major and minor version releases) + +- Version 5.11.0: `networkInterfaces()` added default property and default parameter +- Version 5.10.0: basic `android` support +- Version 5.9.0: `graphics()` added properties (macOS) +- Version 5.8.0: `disksIO()` added waitTime, waitPercent (linux) +- Version 5.7.0: `diskLayout()` added S.M.A.R.T for Windows (if installed) +- Version 5.6.0: `cpuTemperature()` added added socket and chipset temp (linux) +- Version 5.5.0: `dockerVolumes()` added +- Version 5.4.0: `dockerImages()` added +- Version 5.3.0: `osInfo()` added remoteSession (win only) +- Version 5.2.0: `wifiInterfaces()` and `wifiConnections()` added +- Version 5.1.0: `memLayout()` added ECC flag, `bios()` added language, features (linux) +- Version 5.0.0: new version 5 - attention there are some breaking changes. See [detailed version 5 changes here][changes5-url]. +- ... + +You can find all changes here: [detailed changelog][changelog-url] + +## Core concept + +[Node.js][nodejs-url] comes with some basic OS information, but I always wanted a little more. So I came up to write this little library. This library is still work in progress. It is supposed to be used as a backend/server-side library (will definitely not work within a browser). It requires node.js version 4.0 and above. + +I was able to test it on several Debian, Raspbian, Ubuntu distributions as well as macOS (Mavericks, Yosemite, El Captain, Sierra, High Sierra, Mojave, Catalina, Big Sur) and some Windows 7, Windows 8, Windows 10, FreeBSD, OpenBSD, NetBSD and SunOS machines. Not all functions are supported on all operating systems. Have a look at the function reference in the docs to get further details. + +If you have comments, suggestions & reports, please feel free to contact me! + + +I also created a nice little command line tool called [mmon][mmon-github-url] (micro-monitor) for Linux and macOS, also available via [github][mmon-github-url] and [npm][mmon-npm-url] + + +## Reference + +### Function Reference and OS Support + +Full function reference with examples can be found at [https://systeminformation.io][systeminformation-url]. + +#### 1. General + +| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | +| --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | +| si.version() | : string | X | X | X | X | X | lib version (no callback/promise) | +| si.time() | {...} | X | X | X | X | X | (no callback/promise) | +| | current | X | X | X | X | X | local (server) time | +| | uptime | X | X | X | X | X | uptime in number of seconds | +| | timezone | X | X | X | X | X | e.g. GMT+0200 | +| | timezoneName | X | X | X | X | X | e.g. CEST | + +#### 2. System (HW) + +| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | +| --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | +| si.system(cb) | {...} | X | X | X | X | | hardware information | +| | manufacturer | X | X | X | X | | e.g. 'MSI' | +| | model | X | X | X | X | | model/product e.g. 'MS-7823' | +| | version | X | X | X | X | | version e.g. '1.0' | +| | serial | X | X | X | X | | serial number | +| | uuid | X | X | X | X | | UUID | +| | sku | X | X | X | X | | SKU number | +| | virtual | X | X | | X | | is virtual machine | +| | virtualHost | X | X | | X | | virtual host (if virtual) | +| | raspberry | X | | | | | optional raspberry revision data | +| si.bios(cb) | {...} | X | X | X | X | | bios information | +| | vendor | X | X | X | X | | e.g. 'AMI' | +| | version | X | X | X | X | | version | +| | releaseDate | X | X | | X | | release date | +| | revision | X | X | | X | | revision | +| | serial | X | | | X | | serial | +| si.baseboard(cb) | {...} | X | X | X | X | | baseboard information | +| | manufacturer | X | X | X | X | | e.g. 'ASUS' | +| | model | X | X | X | X | | model / product name | +| | version | X | X | X | X | | version | +| | serial | X | X | X | X | | serial number | +| | assetTag | X | X | X | X | | asset tag | +| | memMax | X | | X | X | | max memory in bytes | +| | memSlots | X | | X | X | | memory slots on baseboard | +| si.chassis(cb) | {...} | X | X | X | X | | chassis information | +| | manufacturer | X | X | X | X | | e.g. 'MSI' | +| | model | X | X | X | X | | model / product name | +| | type | X | X | X | X | | model / product name | +| | version | X | X | X | X | | version | +| | serial | X | X | X | X | | serial number | +| | assetTag | X | X | X | X | | asset tag | +| | sku | | | | X | | SKU number | + +#### 3. CPU + +| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | +| --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | +| si.cpu(cb) | {...} | X | X | X | X | | CPU information| +| | manufacturer | X | X | X | X | | e.g. 'Intel(R)' | +| | brand | X | X | X | X | | e.g. 'Core(TM)2 Duo' | +| | speed | X | X | X | X | | in GHz e.g. '3.40' | +| | speedMin | X | | X | X | | in GHz e.g. '0.80' | +| | speedMax | X | X | X | X | | in GHz e.g. '3.90' | +| | governor | X | | | | | e.g. 'powersave' | +| | cores | X | X | X | X | | # cores | +| | physicalCores | X | X | X | X | | # physical cores | +| | efficiencyCores | | | X | | | # efficiancy cores (ARM only) | +| | performanceCores | | | X | | | # performance cores (ARM only) | +| | processors | X | X | X | X | | # processors | +| | socket | X | X | | X | | socket type e.g. "LGA1356" | +| | vendor | X | X | X | X | | vendor ID | +| | family | X | X | X | X | | processor family | +| | model | X | X | X | X | | processor model | +| | stepping | X | X | X | X | | processor stepping | +| | revision | X | | X | X | | revision | +| | voltage | | X | | | | voltage | +| | flags | X | X | X | X | | CPU flags | +| | virtualization | X | X | X | X | | virtualization supported | +| | cache | X | X | X | X | | cache in bytes (object) | +| | cache.l1d | X | X | X | X | | L1D (data) size | +| | cache.l1i | X | X | X | X | | L1I (instruction) size | +| | cache.l2 | X | X | X | X | | L2 size | +| | cache.l3 | X | X | X | X | | L3 size | +| si.cpuFlags(cb) | : string | X | X | X | X | | CPU flags| +| si.cpuCache(cb) | {...} | X | X | X | X | | CPU cache sizes | +| | l1d | X | X | X | X | | L1D size | +| | l1i | X | X | X | X | | L1I size | +| | l2 | X | X | X | X | | L2 size | +| | l3 | X | X | X | X | | L3 size | +| si.cpuCurrentSpeed(cb) | {...} | X | X | X | X | X | current CPU speed (in GHz)| +| | avg | X | X | X | X | X | avg CPU speed (all cores) | +| | min | X | X | X | X | X | min CPU speed (all cores) | +| | max | X | X | X | X | X | max CPU speed (all cores) | +| | cores | X | X | X | X | X | CPU speed per core (array) | +| si.cpuTemperature(cb) | {...} | X | X | X* | X | | CPU temperature in C (if supported) | +| | main | X | X | X | X | | main temperature (avg) | +| | cores | X | X | X | X | | array of temperatures | +| | max | X | X | X | X | | max temperature | +| | socket | X | | | | | array socket temperatures | +| | chipset | X | | | | | chipset temperature | + +#### 4. Memory + +| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | +| --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | +| si.mem(cb) | {...} | X | X | X | X | X | Memory information (in bytes)| +| | total | X | X | X | X | X | total memory in bytes | +| | free | X | X | X | X | X | not used in bytes | +| | used | X | X | X | X | X | used (incl. buffers/cache) | +| | active | X | X | X | X | X | used actively (excl. buffers/cache) | +| | buffcache | X | X | X | | X | used by buffers+cache | +| | buffers | X | | | | | used by buffers | +| | cached | X | | | | | used by cache | +| | slab | X | | | | | used by slab | +| | available | X | X | X | X | X | potentially available (total - active) | +| | swaptotal | X | X | X | X | X | | +| | swapused | X | X | X | X | X | | +| | swapfree | X | X | X | X | X | | +| si.memLayout(cb) | [{...}] | X | X | X | X | | Memory Layout (array) | +| | [0].size | X | X | X | X | | size in bytes | +| | [0].bank | X | X | | X | | memory bank | +| | [0].type | X | X | X | X | | memory type | +| | [0].clockSpeed | X | X | X | X | | clock speed | +| | [0].formFactor | X | X | | X | | form factor | +| | [0].manufacturer | X | X | X | X | | manufacturer | +| | [0].partNum | X | X | X | X | | part number | +| | [0].serialNum | X | X | X | X | | serial number | +| | [0].voltageConfigured | X | X | | X | | voltage conf. | +| | [0].voltageMin | X | X | | X | | voltage min | +| | [0].voltageMax | X | X | | X | | voltage max | + +#### 5. Battery + +| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | +| --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | +| si.battery(cb) | {...} | X | X | X | X | | battery information | +| | hasBattery | X | X | X | X | | indicates presence of battery | +| | cycleCount | X | | X | | | numbers of recharges | +| | isCharging | X | X | X | X | | indicates if battery is charging | +| | designedCapacity | X | | X | X | | max capacity of battery (mWh) | +| | maxCapacity | X | | X | X | | max capacity of battery (mWh) | +| | currentCapacity | X | | X | X | | current capacity of battery (mWh) | +| | capacityUnit | X | | X | X | | capacity unit (mWh) | +| | voltage | X | | X | X | | current voltage of battery (V) | +| | percent | X | X | X | X | | charging level in percent | +| | timeRemaining | X | | X | | | minutes left (if discharging) | +| | acConnected | X | X | X | X | | AC connected | +| | type | X | | X | | | battery type | +| | model | X | | X | | | model | +| | manufacturer | X | | X | | | manufacturer | +| | serial | X | | X | | | battery serial | + +* See known issues if you have problem with macOS temperature or windows temperature + +#### 6. Graphics + +| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | +| --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | +| si.graphics(cb) | {...} | X | | X | X | | arrays of graphics controllers and displays | +| | controllers[]| X | | X | X | | graphics controllers array | +| | ...[0].vendor | X | | X | X | | e.g. ATI | +| | ...[0].vendorId | | | X | | | vendor ID | +| | ...[0].model | X | | X | X | | graphics controller model | +| | ...[0].deviceId | | | X | | | device ID | +| | ...[0].bus | X | | X | X | | on which bus (e.g. PCIe) | +| | ...[0].vram | X | | X | X | | VRAM size (in MB) | +| | ...[0].vramDynamic | X | | X | X | | true if dynamicly allocated ram | +| | ...[0].external | | | X | | | is external GPU | +| | ...[0].cores | | | X | | | Apple silicon only | +| | ...[0].metalVersion | | | X | | | Apple Metal Version | +| | displays[] | X | | X | X | | monitor/display array | +| | ...[0].vendor | | | | X | | monitor/display vendor | +| | ...[0].vendorId | | | X | | | vendor ID | +| | ...[0].deviceName | | | | X | | e.g. \\\\.\\DISPLAY1 | +| | ...[0].model | X | | X | X | | monitor/display model | +| | ...[0].productionYear | | | X | | | production year | +| | ...[0].serial | | | X | | | serial number | +| | ...[0].displayId | | | X | | | display ID | +| | ...[0].main | X | | X | X| | true if main monitor | +| | ...[0].builtin | X | | X | | | true if built in monitor | +| | ...[0].connection | X | | X | X | | e.g. DisplayPort or HDMI | +| | ...[0].sizeX | X | | | X | | size in mm horizontal | +| | ...[0].sizeY | X | | | X | | size in mm vertical | +| | ...[0].pixelDepth | X | | X | X | | color depth in bits | +| | ...[0].resolutionX | X | | X | X | | pixel horizontal | +| | ...[0].resolutionY | X | | X | X | | pixel vertical | +| | ...[0].currentResX | X | | X | X | | current pixel horizontal | +| | ...[0].currentResY | X | | X | X | | current pixel vertical | +| | ...[0].positionX | | | | X | | display position X | +| | ...[0].positionY | | | | X | | display position Y | +| | ...[0].currentRefreshRate | X | | X | X | | current screen refresh rate | + +#### 7. Operating System + +| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | +| --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | +| si.osInfo(cb) | {...} | X | X | X | X | X | OS information | +| | platform | X | X | X | X | X | 'linux', 'darwin', 'win32', ... | +| | distro | X | X | X | X | X | | +| | release | X | X | X | X | X | | +| | codename | | | X | | | | +| | kernel | X | X | X | X | X | kernel release - same as os.release() | +| | arch | X | X | X | X | X | same as os.arch() | +| | hostname | X | X | X | X | X | same as os.hostname() | +| | fqdn | X | X | X | X | X | FQDN fully qualified domain name | +| | codepage | X | X | X | X | | OS build version | +| | logofile | X | X | X | X | X | e.g. 'apple', 'debian', 'fedora', ... | +| | serial | X | X | X | X | | OS/Host serial number | +| | build | X | | X | X | | OS build version | +| | servicepack | | | | X | | service pack version | +| | uefi | X | X | X | X | | OS started via UEFI | +| | hypervisor | | | | X | | hyper-v enabled? (win only) | +| | remoteSession | | | | X | | runs in remote session (win only) | +| si.uuid(cb) | {...} | X | X | X | X | X | object of several UUIDs | +| | os | X | X | X | X | | os specific UUID | +| | hardware | X | X | X | X | | hardware specific UUID | +| | macs | X | X | X | X | | MAC addresses | +| si.versions(apps, cb) | {...} | X | X | X | X | X | version information (kernel, ssl, node, ...)<br />apps param is optional for detecting<br />only specific apps/libs<br />(string, comma separated) | +| si.shell(cb) | : string | X | X | X | X | | standard shell | +| si.users(cb) | [{...}] | X | X | X | X | X | array of users online | +| | [0].user | X | X | X | X | X | user name | +| | [0].tty | X | X | X | X | X | terminal | +| | [0].date | X | X | X | X | X | login date | +| | [0].time | X | X | X | X | X | login time | +| | [0].ip | X | X | X | | X | ip address (remote login) | +| | [0].command | X | X | X | | X | last command or shell | + +#### 8. Current Load, Processes & Services + +| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | +| --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | +| si.currentLoad(cb) | {...} | X | | X | X | X | CPU-Load | +| | avgLoad | X | | X | | X | average load | +| | currentLoad | X | | X | X | X | CPU load in % | +| | currentLoadUser | X | | X | X | X | CPU load user in % | +| | currentLoadSystem | X | | X | X | X | CPU load system in % | +| | currentLoadNice | X | | X | X | X | CPU load nice in % | +| | currentLoadIdle | X | | X | X | X | CPU load idle in % | +| | currentLoadIrq | X | | X | X | X | CPU load system in % | +| | rawCurrentLoad... | X | | X | X | X | CPU load raw values (ticks) | +| | cpus[] | X | | X | X | X | current loads per CPU in % + raw ticks | +| si.fullLoad(cb) | : integer | X | | X | X | | CPU full load since bootup in % | +| si.processes(cb) | {...} | X | X | X | X | X | # running processes | +| | all | X | X | X | X | X | # of all processes | +| | running | X | X | X | | X | # of all processes running | +| | blocked | X | X | X | | X | # of all processes blocked | +| | sleeping | X | X | X | | X | # of all processes sleeping | +| | unknown | | | | X | | # of all processes unknown status | +| | list[] | X | X | X | X | X | list of all processes incl. details | +| | ...[0].pid | X | X | X | X | X | process PID | +| | ...[0].parentPid | X | X | X | X | X | parent process PID | +| | ...[0].name | X | X | X | X | X | process name | +| | ...[0].cpu | X | X | X | X | X | process % CPU usage | +| | ...[0].cpuu | X | X | | X | | process % CPU usage (user) | +| | ...[0].cpus | X | X | | X | | process % CPU usage (system) | +| | ...[0].mem | X | X | X | X | X | process memory % | +| | ...[0].priority | X | X | X | X | X | process priotity | +| | ...[0].memVsz | X | X | X | X | X | process virtual memory size | +| | ...[0].memRss | X | X | X | X | X | process mem resident set size | +| | ...[0].nice | X | X | X | | X | process nice value | +| | ...[0].started | X | X | X | X | X | process start time | +| | ...[0].state | X | X | X | X | X | process state (e.g. sleeping) | +| | ...[0].tty | X | X | X | | X | tty from which process was started | +| | ...[0].user | X | X | X | | X | user who started process | +| | ...[0].command | X | X | X | X | X | process starting command | +| | ...[0].params | X | X | X | | X | process params | +| | ...[0].path | X | X | X | X | X | process path | +| | proc | X | X | X | X | | process name | +| | pid | X | X | X | X | | PID | +| | pids | X | X | X | X | | additional pids | +| | cpu | X | X | X | X | | process % CPU | +| | mem | X | X | X | X | | process % MEM | +| si.services('mysql, apache2', cb) | [{...}] | X | X | X | X | | pass comma separated string of services<br>pass "*" for ALL services (linux/win only) | +| | [0].name | X | X | X | X | | name of service | +| | [0].running | X | X | X | X | | true / false | +| | [0].startmode | | | | X | | manual, automatic, ... | +| | [0].pids | X | X | X | X | | pids | +| | [0].cpu | X | X | X | | | process % CPU | +| | [0].mem | X | X | X | | | process % MEM | +| si.processLoad('mysql, apache2', cb) | [{...}] | X | X | X | X | | pass comma separated string of processes<br>pass "*" for ALL processes (linux/win only) | +| | [0].proc | X | X | X | X | | name of process | +| | [0].pids | X | X | X | X | | pids | +| | [0].cpu | X | X | X | | | process % CPU | +| | [0].mem | X | X | X | | | process % MEM | + +#### 9. File System + +| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | +| --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | +| si.diskLayout(cb) | [{...}] | X | | X | X | | physical disk layout (array) | +| | [0].device | X | | X | | | e.g. /dev/sda | +| | [0].type | X | | X | X | | HD, SSD, NVMe | +| | [0].name | X | | X | X | | disk name | +| | [0].vendor | X | | | X | | vendor/producer | +| | [0].size | X | | X | X | | size in bytes | +| | [0].bytesPerSector | | | | X | | bytes per sector | +| | [0].totalCylinders | | | | X | | total cylinders | +| | [0].totalHeads | | | | X | | total heads | +| | [0].totalSectors | | | | X | | total sectors | +| | [0].totalTracks | | | | X | | total tracks | +| | [0].tracksPerCylinder | | | | X | | tracks per cylinder | +| | [0].sectorsPerTrack | | | | X | | sectors per track | +| | [0].firmwareRevision | X | | X | X | | firmware revision | +| | [0].serialNum | X | | X | X | | serial number | +| | [0].interfaceType | X | | | X | | SATA, PCIe, ... | +| | [0].smartStatus | X | | X | X | | S.M.A.R.T Status (see Known Issues) | +| | [0].temperature | X | | | | | S.M.A.R.T temperature | +| | [0].smartData | X | | | X | | full S.M.A.R.T data from smartctl<br>requires at least smartmontools 7.0 | +| si.blockDevices(cb) | [{...}] | X | | X | X | | returns array of disks, partitions,<br>raids and roms | +| | [0].name | X | | X | X | | name | +| | [0].type | X | | X | X | | type | +| | [0].fstype | X | | X | X | | file system type (e.g. ext4) | +| | [0].mount | X | | X | X | | mount point | +| | [0].size | X | | X | X | | size in bytes | +| | [0].physical | X | | X | X | | physical type (HDD, SSD, CD/DVD) | +| | [0].uuid | X | | X | X | | UUID | +| | [0].label | X | | X | X | | label | +| | [0].model | X | | X | | | model | +| | [0].serial | X | | | X | | serial | +| | [0].removable | X | | X | X | | serial | +| | [0].protocol | X | | X | | | protocol (SATA, PCI-Express, ...) | +| si.disksIO(cb) | {...} | X | | X | | | current transfer stats | +| | rIO | X | | X | | | read IOs on all mounted drives | +| | wIO | X | | X | | | write IOs on all mounted drives | +| | tIO | X | | X | | | write IOs on all mounted drives | +| | rIO_sec | X | | X | | | read IO per sec (* see notes) | +| | wIO_sec | X | | X | | | write IO per sec (* see notes) | +| | tIO_sec | X | | X | | | total IO per sec (* see notes) | +| | rWaitTime | X | | | | | read IO request time (* see notes) | +| | wWaitTime | X | | | | | write IO request time (* see notes) | +| | tWaitTime | X | | | | | total IO request time (* see notes) | +| | rWaitPercent | X | | | | | read IO request time percent (* see notes) | +| | wWaitPercent | X | | | | | write IO request time percent (* see notes) | +| | tWaitPercent | X | | | | | total IO request time percent (* see notes) | +| | ms | X | | X | | | interval length (for per second values) | +| si.fsSize(cb) | [{...}] | X | X | X | X | | returns array of mounted file systems | +| | [0].fs | X | X | X | X | | name of file system | +| | [0].type | X | X | X | X | | type of file system | +| | [0].size | X | X | X | X | | sizes in bytes | +| | [0].used | X | X | X | X | | used in bytes | +| | [0].available | X | X | X | X | | used in bytes | +| | [0].use | X | X | X | X | | used in % | +| | [0].mount | X | X | X | X | | mount point | +| si.fsOpenFiles(cb) | {...} | X | X | X | | | count max/allocated file descriptors | +| | max | X | X | X | | | max file descriptors | +| | allocated | X | X | X | | | current open files count | +| | available | X | X | X | | | count available | +| si.fsStats(cb) | {...} | X | | X | | | current transfer stats | +| | rx | X | | X | | | bytes read since startup | +| | wx | X | | X | | | bytes written since startup | +| | tx | X | | X | | | total bytes read + written since startup | +| | rx_sec | X | | X | | | bytes read / second (* see notes) | +| | wx_sec | X | | X | | | bytes written / second (* see notes) | +| | tx_sec | X | | X | | | total bytes reads + written / second | +| | ms | X | | X | | | interval length (for per second values) | + +#### 10. USB + +| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | +| --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | +| si.usb(cb) | [{...}] | X | X | X | X | | get detected USB devices | +| | [0].bus | X | | | | | USB bus | +| | [0].deviceId | X | | | | | bus device id | +| | [0].id | X | | X | X | | internal id | +| | [0].name | X | | X | X | | name | +| | [0].type | X | | X | X | | name | +| | [0].removable | | | X | | | is removable | +| | [0].vendor | X | | X | | | vendor | +| | [0].manufacturer | X | | X | X | | manifacturer | +| | [0].maxPower | X | | | | | max power | +| | [0].default | X | | X | X | | is default printer | +| | [0].serialNumber | | | X | | | serial number | + +#### 11. Printer + +| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | +| --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | +| si.printer(cb) | [{...}] | X | X | X | X | | get printer information | +| | [0].id | X | | X | X | | internal id | +| | [0].name | X | | X | X | | name | +| | [0].model | X | | X | X | | model | +| | [0].uri | X | | X | | | printer URI | +| | [0].uuid | X | | | | | printer UUID | +| | [0].status | X | | X | X | | printer status (e.g. idle) | +| | [0].local | X | | X | X | | is local printer | +| | [0].default | | | X | X | | is default printer | +| | [0].shared | X | | X | X | | is shared printer | + +#### 12. Audio + +| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | +| --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | +| si.audio(cb) | [{...}] | X | X | X | X | | get printer information | +| | [0].id | X | | X | X | | internal id | +| | [0].name | X | | X | X | | name | +| | [0].manufacturer | X | | X | X | | manufacturer | +| | [0].revision | X | | | | | revision | +| | [0].driver | X | | | | | driver | +| | [0].default | | | X | X | | is default | +| | [0].channel | X | | X | | | channel e.g. USB, HDMI, ... | +| | [0].type | X | | X | X | | type e.g. Speaker | +| | [0].in | | | X | X | | is input channel | +| | [0].out | | | X | X | | is output channel | +| | [0].interfaceType | X | | X | X | | interface type (PCIe, USB, HDMI, ...) | +| | [0].status | X | | X | X | | printer status (e.g. idle) | + +#### 13. Network related functions + +| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | +| --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | +| si.networkInterfaces(cb) | [{...}] | X | X | X | X | X | array of network interfaces<br>With the 'default' parameter it returns<br>only the default interface | +| | [0].iface | X | X | X | X | X | interface | +| | [0].ifaceName | X | X | X | X | X | interface name (differs on Windows) | +| | [0].default | X | X | X | X | X | true if this is the default interface | +| | [0].ip4 | X | X | X | X | X | ip4 address | +| | [0].ip4subnet | X | X | X | X | X | ip4 subnet mask | +| | [0].ip6 | X | X | X | X | X | ip6 address | +| | [0].ip6subnet | X | X | X | X | X | ip6 subnet mask | +| | [0].mac | X | X | X | X | X | MAC address | +| | [0].internal | X | X | X | X | X | true if internal interface | +| | [0].virtual | X | X | X | X | X | true if virtual interface | +| | [0].operstate | X | | X | X | | up / down | +| | [0].type | X | | X | X | | wireless / wired | +| | [0].duplex | X | | X | | | duplex | +| | [0].mtu | X | | X | | | maximum transmission unit | +| | [0].speed | X | | X | X | | speed in MBit / s | +| | [0].dhcp | X | | X | X | | IP address obtained by DHCP | +| | [0].dnsSuffix | X | | | X | | DNS suffix | +| | [0].ieee8021xAuth | X | | | X | | IEEE 802.1x auth | +| | [0].ieee8021xState | X | | | X | | IEEE 802.1x state | +| | [0].carrierChanges | X | | | | | # changes up/down | +| si.networkInterfaceDefault(cb) | : string | X | X | X | X | X | get name of default network interface | +| si.networkGatewayDefault(cb) | : string | X | X | X | X | X | get default network gateway | +| si.networkStats(ifaces,cb) | [{...}] | X | X | X | X | | current network stats of given interfaces<br>iface list: space or comma separated<br>iface parameter is optional<br>defaults to first external network interface,<br />Pass '*' for all interfaces | +| | [0].iface | X | X | X | X | | interface | +| | [0].operstate | X | X | X | X | | up / down | +| | [0].rx_bytes | X | X | X | X | | received bytes overall | +| | [0].rx_dropped | X | X | X | X | | received dropped overall | +| | [0].rx_errors | X | X | X | X | | received errors overall | +| | [0].tx_bytes | X | X | X | X | | transferred bytes overall | +| | [0].tx_dropped | X | X | X | X | | transferred dropped overall | +| | [0].tx_errors | X | X | X | X | | transferred errors overall | +| | [0].rx_sec | X | X | X | X | | received bytes / second (* see notes) | +| | [0].tx_sec | X | X | X | X | | transferred bytes per second (* see notes) | +| | [0].ms | X | X | X | X | | interval length (for per second values) | +| si.networkConnections(cb) | [{...}] | X | X | X | X | | current network network connections<br>returns an array of all connections| +| | [0].protocol | X | X | X | X | | tcp or udp | +| | [0].localAddress | X | X | X | X | | local address | +| | [0].localPort | X | X | X | X | | local port | +| | [0].peerAddress | X | X | X | X | | peer address | +| | [0].peerPort | X | X | X | X | | peer port | +| | [0].state | X | X | X | X | | like ESTABLISHED, TIME_WAIT, ... | +| | [0].pid | X | X | X | X | | process ID | +| | [0].process | X | X | | | | process name | +| si.inetChecksite(url, cb) | {...} | X | X | X | X | X | response-time (ms) to fetch given URL | +| | url | X | X | X | X | X | given url | +| | ok | X | X | X | X | X | status code OK (2xx, 3xx) | +| | status | X | X | X | X | X | status code | +| | ms | X | X | X | X | X | response time in ms | +| si.inetLatency(host, cb) | : number | X | X | X | X | X | response-time (ms) to external resource<br>host parameter is optional (default 8.8.8.8)| + +#### 14. Wifi + +| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | +| --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | +| si.wifiNetworks(cb) | [{...}] | X | | X | X | | array of available wifi networks | +| | [0].ssid | X | | X | X | | Wifi network SSID | +| | [0].bssid | X | | X | X | | BSSID (mac) | +| | [0].mode | X | | | | | mode | +| | [0].channel | X | | X | X | | channel | +| | [0].frequency | X | | X | X | | frequengy in MHz | +| | [0].signalLevel | X | | X | X | | signal level in dB | +| | [0].quality | X | | X | X | | quaility in % | +| | [0].security | X | | X | X | | array e.g. WPA, WPA-2 | +| | [0].wpaFlags | X | | X | X | | array of WPA flags | +| | [0].rsnFlags | X | | | | | array of RDN flags | +| si.wifiInterfaces(cb) | [{...}] | X | | X | X | | array of detected wifi interfaces | +| | [0].id | X | | X | X | | ID | +| | [0].iface | X | | X | X | | interface | +| | [0].model | X | | X | X | | model | +| | [0].vendor | X | | X | X | | vendor | +| | [0].mac | X | | X | X | | MAC address | +| si.wifiConnections(cb) | [{...}] | X | | X | X | | array of active wifi connections | +| | [0].id | X | | X | X | | ID | +| | [0].iface | X | | X | X | | interface | +| | [0].name | X | | X | X | | name | +| | [0].mode | X | | X | X | | model | +| | [0].bssid | X | | X | X | | BSSID (mac) | +| | [0].mode | X | | | | | mode | +| | [0].channel | X | | X | X | | channel | +| | [0].frequency | X | | X | X | | frequengy in MHz | +| | [0].signalLevel | X | | X | X | | signal level in dB | +| | [0].quality | X | | X | X | | quaility in % | +| | [0].security | X | | X | X | | array e.g. WPA, WPA-2 | +| | [0].txRate | X | | X | X | | transfer rate MBit/s | + +#### 15. Bluetooth + +| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | +| --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | +| si.bluetoothDevices(cb) | [{...}] | X | | X | X | | ... | +| | [0].device | | | X | | | device name | +| | [0].name | X | | X | X | | name | +| | [0].macDevice | X | | X | | | MAC address device | +| | [0].macHost | X | | X | | | MAC address host | +| | [0].batteryPercent | | | X | | | battery level percent | +| | [0].manufacturer | | | X | X | | manufacturer | +| | [0].type | X | | X | X | | typoe of bluetooth device | +| | [0].connected | X | | X | | | is connected | + +#### 16. Docker + +| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | +| --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | +| si.dockerInfo(cb) | {...} | X | X | X | X | X | returns general docker info | +| | id | X | X | X | X | X | Docker ID | +| | containers | X | X | X | X | X | number of containers | +| | containersRunning | X | X | X | X | X | number of running containers | +| | containersPaused | X | X | X | X | X | number of paused containers | +| | containersStopped | X | X | X | X | X | number of stopped containers | +| | images | X | X | X | X | X | number of images | +| | driver | X | X | X | X | X | driver (e.g. 'devicemapper', 'overlay2') | +| | memoryLimit | X | X | X | X | X | has memory limit | +| | swapLimit | X | X | X | X | X | has swap limit | +| | kernelMemory | X | X | X | X | X | has kernal memory | +| | cpuCfsPeriod | X | X | X | X | X | has CpuCfsPeriod | +| | cpuCfsQuota | X | X | X | X | X | has CpuCfsQuota | +| | cpuShares | X | X | X | X | X | has CPUShares | +| | cpuSet | X | X | X | X | X | has CPUShares | +| | ipv4Forwarding | X | X | X | X | X | has IPv4Forwarding | +| | bridgeNfIptables | X | X | X | X | X | has BridgeNfIptables | +| | bridgeNfIp6tables | X | X | X | X | X | has BridgeNfIp6tables | +| | debug | X | X | X | X | X | Debug on | +| | nfd | X | X | X | X | X | named data networking forwarding daemon | +| | oomKillDisable | X | X | X | X | X | out-of-memory kill disabled | +| | ngoroutines | X | X | X | X | X | number NGoroutines | +| | systemTime | X | X | X | X | X | docker SystemTime | +| | loggingDriver | X | X | X | X | X | logging driver e.g. 'json-file' | +| | cgroupDriver | X | X | X | X | X | cgroup driver e.g. 'cgroupfs' | +| | nEventsListener | X | X | X | X | X | number NEventsListeners | +| | kernelVersion | X | X | X | X | X | docker kernel version | +| | operatingSystem | X | X | X | X | X | docker OS e.g. 'Docker for Mac' | +| | osType | X | X | X | X | X | OSType e.g. 'linux' | +| | architecture | X | X | X | X | X | architecture e.g. x86_64 | +| | ncpu | X | X | X | X | X | number of CPUs | +| | memTotal | X | X | X | X | X | memory total | +| | dockerRootDir | X | X | X | X | X | docker root directory | +| | httpProxy | X | X | X | X | X | http proxy | +| | httpsProxy | X | X | X | X | X | https proxy | +| | noProxy | X | X | X | X | X | NoProxy | +| | name | X | X | X | X | X | Name | +| | labels | X | X | X | X | X | array of labels | +| | experimentalBuild | X | X | X | X | X | is experimental build | +| | serverVersion | X | X | X | X | X | server version | +| | clusterStore | X | X | X | X | X | cluster store | +| | clusterAdvertise | X | X | X | X | X | cluster advertise | +| | defaultRuntime | X | X | X | X | X | default runtime e.g. 'runc' | +| | liveRestoreEnabled | X | X | X | X | X | live store enabled | +| | isolation | X | X | X | X | X | isolation | +| | initBinary | X | X | X | X | X | init binary | +| | productLicense | X | X | X | X | X | product license | +| si.dockerImages(all, cb) | [{...}] | X | X | X | X | X | returns array of top level/all docker images | +| | [0].id | X | X | X | X | X | image ID | +| | [0].container | X | X | X | X | X | container ID | +| | [0].comment | X | X | X | X | X | comment | +| | [0].os | X | X | X | X | X | OS | +| | [0].architecture | X | X | X | X | X | architecture | +| | [0].parent | X | X | X | X | X | parent ID | +| | [0].dockerVersion | X | X | X | X | X | docker version | +| | [0].size | X | X | X | X | X | image size | +| | [0].sharedSize | X | X | X | X | X | shared size | +| | [0].virtualSize | X | X | X | X | X | virtual size | +| | [0].author | X | X | X | X | X | author | +| | [0].created | X | X | X | X | X | created date / time | +| | [0].containerConfig | X | X | X | X | X | container config object | +| | [0].graphDriver | X | X | X | X | X | graph driver object | +| | [0].repoDigests | X | X | X | X | X | repo digests array | +| | [0].repoTags | X | X | X | X | X | repo tags array | +| | [0].config | X | X | X | X | X | config object | +| | [0].rootFS | X | X | X | X | X | root fs object | +| si.dockerContainers(all, cb) | [{...}] | X | X | X | X | X | returns array of active/all docker containers | +| | [0].id | X | X | X | X | X | ID of container | +| | [0].name | X | X | X | X | X | name of container | +| | [0].image | X | X | X | X | X | name of image | +| | [0].imageID | X | X | X | X | X | ID of image | +| | [0].command | X | X | X | X | X | command | +| | [0].created | X | X | X | X | X | creation time (unix) | +| | [0].started | X | X | X | X | X | creation time (unix) | +| | [0].finished | X | X | X | X | X | creation time (unix) | +| | [0].createdAt | X | X | X | X | X | creation date time string | +| | [0].startedAt | X | X | X | X | X | creation date time string | +| | [0].finishedAt | X | X | X | X | X | creation date time string | +| | [0].state | X | X | X | X | X | created, running, exited | +| | [0].ports | X | X | X | X | X | array of ports | +| | [0].mounts | X | X | X | X | X | array of mounts | +| si.dockerContainerStats(ids, cb) | [{...}] | X | X | X | X | X | statistics for specific containers<br>container IDs: space or comma separated,<br>pass '*' for all containers| +| | [0].id | X | X | X | X | X | Container ID | +| | [0].memUsage | X | X | X | X | X | memory usage in bytes | +| | [0].memLimit | X | X | X | X | X | memory limit (max mem) in bytes | +| | [0].memPercent | X | X | X | X | X | memory usage in percent | +| | [0].cpuPercent | X | X | X | X | X | cpu usage in percent | +| | [0].pids | X | X | X | X | X | number of processes | +| | [0].netIO.rx | X | X | X | X | X | received bytes via network | +| | [0].netIO.wx | X | X | X | X | X | sent bytes via network | +| | [0].blockIO.r | X | X | X | X | X | bytes read from BlockIO | +| | [0].blockIO.w | X | X | X | X | X | bytes written to BlockIO | +| | [0].cpuStats | X | X | X | X | X | detailed cpu stats | +| | [0].percpuStats | X | X | X | X | X | detailed per cpu stats | +| | [0].memoryStats | X | X | X | X | X | detailed memory stats | +| | [0].networks | X | X | X | X | X | detailed network stats per interface | +| si.dockerContainerProcesses(id, cb) | [{...}] | X | X | X | X | X | array of processes inside a container | +| | [0].pidHost | X | X | X | X | X | process ID (host) | +| | [0].ppid | X | X | X | X | X | parent process ID | +| | [0].pgid | X | X | X | X | X | process group ID | +| | [0].user | X | X | X | X | X | effective user name | +| | [0].ruser | X | X | X | X | X | real user name | +| | [0].group | X | X | X | X | X | effective group name | +| | [0].rgroup | X | X | X | X | X | real group name | +| | [0].stat | X | X | X | X | X | process state | +| | [0].time | X | X | X | X | X | accumulated CPU time | +| | [0].elapsed | X | X | X | X | X | elapsed running time | +| | [0].nice | X | X | X | X | X | nice value | +| | [0].rss | X | X | X | X | X | resident set size | +| | [0].vsz | X | X | X | X | X | virtual size in Kbytes | +| | [0].command | X | X | X | X | X | command and arguments | +| si.dockerVolumes(cb) | [{...}] | returns array of all docker volumes | +| | [0].name | X | X | X | X | X | volume name | +| | [0].driver | X | X | X | X | X | driver | +| | [0].labels | X | X | X | X | X | labels object | +| | [0].mountpoint | X | X | X | X | X | mountpoint | +| | [0].options | X | X | X | X | X | options | +| | [0].scope | X | X | X | X | X | scope | +| | [0].created | X | X | X | X | X | created at | +| si.dockerAll(cb) | {...} | X | X | X | X | X | list of all containers including their stats<br>and processes in one single array | + +#### 17. Virtual Box + +| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | +| --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | +| si.vboxInfo(cb) | [{...}] | X | X | X | X | X | returns array general virtual box info | +| | [0].id | X | X | X | X | X | virtual box ID | +| | [0].name | X | X | X | X | X | name | +| | [0].running | X | X | X | X | X | vbox is running | +| | [0].started | X | X | X | X | X | started date time | +| | [0].runningSince | X | X | X | X | X | running since (secs) | +| | [0].stopped | X | X | X | X | X | stopped date time | +| | [0].stoppedSince | X | X | X | X | X | stopped since (secs) | +| | [0].guestOS | X | X | X | X | X | Guest OS | +| | [0].hardwareUUID | X | X | X | X | X | Hardware UUID | +| | [0].memory | X | X | X | X | X | Memory in MB | +| | [0].vram | X | X | X | X | X | VRAM in MB | +| | [0].cpus | X | X | X | X | X | CPUs | +| | [0].cpuExepCap | X | X | X | X | X | CPU exec cap | +| | [0].cpuProfile | X | X | X | X | X | CPU profile | +| | [0].chipset | X | X | X | X | X | chipset | +| | [0].firmware | X | X | X | X | X | firmware | +| | [0].pageFusion | X | X | X | X | X | page fusion | +| | [0].configFile | X | X | X | X | X | config file | +| | [0].snapshotFolder | X | X | X | X | X | snapshot folder | +| | [0].logFolder | X | X | X | X | X | log folder path | +| | [0].hpet | X | X | X | X | X | HPET | +| | [0].pae | X | X | X | X | X | PAE | +| | [0].longMode | X | X | X | X | X | long mode | +| | [0].tripleFaultReset | X | X | X | X | X | triple fault reset | +| | [0].apic | X | X | X | X | X | APIC | +| | [0].x2Apic | X | X | X | X | X | X2APIC | +| | [0].acpi | X | X | X | X | X | ACPI | +| | [0].ioApic | X | X | X | X | X | IOAPIC | +| | [0].biosApicMode | X | X | X | X | X | BIOS APIC mode | +| | [0].bootMenuMode | X | X | X | X | X | boot menu Mode | +| | [0].bootDevice1 | X | X | X | X | X | bootDevice1 | +| | [0].bootDevice2 | X | X | X | X | X | bootDevice2 | +| | [0].bootDevice3 | X | X | X | X | X | bootDevice3 | +| | [0].bootDevice4 | X | X | X | X | X | bootDevice4 | +| | [0].timeOffset | X | X | X | X | X | time Offset | +| | [0].rtc | X | X | X | X | X | RTC | + +#### 16. "Get All / Observe" - functions + +| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | +| --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | +| si.getStaticData(cb) | {...} | X | X | X | X | X | all static data at once | +| si.getDynamicData(srv,iface,cb) | {...} | X | X | X | X | X | all dynamic data at once<br>Specify services and interfaces to monitor<br>Defaults to first external network interface<br>Pass "*" for ALL services (linux/win only)<br>Pass "*" for ALL network interfaces | +| si.getAllData(srv,iface,cb) | {...} | X | X | X | X | X | all data at once<br>Specify services and interfaces to monitor<br>Defaults to first external network interface<br>Pass "*" for ALL services (linux/win only)<br>Pass "*" for ALL network interfaces | +| si.get(valueObject,cb) | {...} | X | X | X | X | X | get partial system info data at once<br>In valueObject you can define<br>all values, you want to get back <br>(see documentation for details) | +| si.observe(valueObject,interval,cb) | - | X | X | X | X | X | Observe a defined value object<br>call callback on changes<br>polling interval in milliseconds | + +### cb: Asynchronous Function Calls (callback) + +Remember: all functions (except `version` and `time`) are implemented as asynchronous functions! There are now three ways to consume them: + +**Callback Style** + +```js +const si = require('systeminformation'); + +si.cpu(function(data) { + console.log('CPU Information:'); + console.log('- manufucturer: ' + data.manufacturer); + console.log('- brand: ' + data.brand); + console.log('- speed: ' + data.speed); + console.log('- cores: ' + data.cores); + console.log('- physical cores: ' + data.physicalCores); + console.log('...'); +}) +``` + +### Promises + +**Promises Style** is new in version 3.0. + +When omitting callback parameter (cb), then you can use all function in a promise oriented way. All functions (except of `version` and `time`) are returning a promise, that you can consume: + +```js +const si = require('systeminformation'); + +si.cpu() + .then(data => { + console.log('CPU Information:'); + console.log('- manufucturer: ' + data.manufacturer); + console.log('- brand: ' + data.brand); + console.log('- speed: ' + data.speed); + console.log('- cores: ' + data.cores); + console.log('- physical cores: ' + data.physicalCores); + console.log('...'); + }) + .catch(error => console.error(error)); +``` + +### Async / Await + +**Using async / await** (available since node v7.6) + +Since node v7.6 you can also use the `async` / `await` pattern. The above example would then look like this: + +```js +const si = require('systeminformation'); + +async function cpuData() { + try { + const data = await si.cpu(); + console.log('CPU Information:'); + console.log('- manufucturer: ' + data.manufacturer); + console.log('- brand: ' + data.brand); + console.log('- speed: ' + data.speed); + console.log('- cores: ' + data.cores); + console.log('- physical cores: ' + data.physicalCores); + console.log('...'); + } catch (e) { + console.log(e) + } +} +``` + +## Known Issues + +#### macOS - Temperature Sensor + +To be able to measure temperature on macOS I created a little additional package. Due to some difficulties +in NPM with `optionalDependencies` I unfortunately was getting unexpected warnings on other platforms. +So I decided to drop this optional dependency for macOS - so by default, you will not get correct values. + +This additional package will unfortunately NOT work on Apple Silicon M1 machines. + +But if you need to detect macOS temperature just run the following additional +installation command: + +```bash +$ npm install osx-temperature-sensor --save +``` + +`systeminformation` will then detect this additional library and return the temperature when calling systeminformations standard function `cpuTemperature()` + +#### Windows Temperature, Battery, ... + +`get-WmiObject` - which is used to determine temperature and battery sometimes needs to be run with admin +privileges. So if you do not get any values, try to run it again with according +privileges. If you still do not get any values, your system might not support this feature. +In some cases we also discovered that `get-WmiObject` returned incorrect temperature values. + +#### Linux Temperature + +In some cases you need to install the Linux `sensors` package to be able to measure temperature +e.g. on DEBIAN based systems by running `sudo apt-get install lm-sensors` + +#### Linux S.M.A.R.T. Status + +To be able to detect S.M.A.R.T. status on Linux you need to install `smartmontools`. On DEBIAN based Linux distributions you can install it by running `sudo apt-get install smartmontools` + +#### Windows Encoding Issues +I now reimplemented all windows functions to avoid encoding problems (special chacarters). And as Windows 11 now droppend `wmic` support, I had to move completely to `powershell`. Be sure that powershell version 5+ is installed on your machine. On older Windows versions (7, 8) you might still see encoding problems due to the old powershell version. +## *: Additional Notes + +In `fsStats()`, `disksIO()` and `networkStats()` the results / sec. values (rx_sec, IOPS, ...) are calculated correctly beginning +with the second call of the function. It is determined by calculating the difference of transferred bytes / IOs +divided by the time between two calls of the function. + +The first time you are calling one of these functions, you will get `null` for transfer rates. The second time, you should then get statistics based on the time between the two calls ... + +So basically, if you e.g. need a value for network stats every second, your code should look like this: + +```js +const si = require('systeminformation'); + +setInterval(function() { + si.networkStats().then(data => { + console.log(data); + }) +}, 1000) +``` + +Beginning with the second call, you get network transfer values per second. + +## Finding new issues + +I am happy to discuss any comments and suggestions. Please feel free to contact me if you see any possibility of improvement! + + +## Comments + +If you have ideas or comments, please do not hesitate to contact me. + + +Happy monitoring! + +Sincerely, + +Sebastian Hildebrandt, [+innovations](http://www.plus-innovations.com) + +## Credits + +Written by Sebastian Hildebrandt [sebhildebrandt](https://github.com/sebhildebrandt) + +#### Contributors + +- Guillaume Legrain [glegrain](https://github.com/glegrain) +- Riccardo Novaglia [richy24](https://github.com/richy24) +- Quentin Busuttil [Buzut](https://github.com/Buzut) +- lapsio [lapsio](https://github.com/lapsio) +- csy [csy](https://github.com/csy1983) +- Tiago Roldão [tiagoroldao](https://github.com/tiagoroldao) +- dragonjet [dragonjet](https://github.com/dragonjet) +- Adam Reis [adamreisnz](https://github.com/adamreisnz) +- Jimi M [ItsJimi](https://github.com/ItsJimi) +- Git² [GitSquared](https://github.com/GitSquared) +- weiyin [weiyin](https://github.com/weiyin) +- Jorai Rijsdijk [Erackron](https://github.com/Erackron) +- Rasmus Porsager [porsager](https://github.com/porsager) +- Nathan Patten [nrpatten](https://github.com/nrpatten) +- Juan Campuzano [juancampuzano](https://github.com/juancampuzano) +- Ricardo Polo [ricardopolo](https://github.com/ricardopolo) +- Miłosz Dźwigała [mily20001](https://github.com/mily20001) +- cconley717 [cconley717](https://github.com/cconley717) +- Maria Camila Cubides [MariaCamilaCubides](https://github.com/MariaCamilaCubides) +- Aleksander Krasnicki [plakak](https://github.com/plakak) +- Alexis Tyler [OmgImAlexis](https://github.com/OmgImAlexis) +- Simon Smith [si458](https://github.com/si458) + +OSX Temperature: credits here are going to: + +- Frank Stock [pcafstockf](https://github.com/pcafstockf) - for his work on [smc-code][smc-code-url] + +## Copyright Information + +Linux is a registered trademark of Linus Torvalds. Apple, macOS, OS X are registered trademarks of Apple Inc., +Windows is a registered trademark of Microsoft Corporation. Node.js is a trademark of Joyent Inc., +Intel is a trademark of Intel Corporation, AMD is a trademark of Advanced Micro Devices Inc., +Raspberry Pi is a trademark of the Raspberry Pi Foundation, Debian is a trademark of the Debian Project, +Ubuntu is a trademark of Canonical Ltd., FreeBSD is a registered trademark of The FreeBSD Foundation, +NetBSD is a registered trademark of The NetBSD Foundation, Docker is a trademark of Docker, Inc., Sun, +Solaris, OpenSolaris and registered trademarks of Sun Microsystems, VMware is a trademark of VMware Inc, +Virtual Box is a trademark of Oracle Corporation, Xen is a registered trademark of Xen Project, +QEMU is a trademark of Fabrice Bellard, bochs is a trademark of The Bochs Project, USB and USB Logo +are trademarks of USB Implementation Forum, Bluetooth and Bluetooth Logo are trademarks of Bluetooth SIG, +Android is a trademark of Google LLC. + +All other trademarks are the property of their respective owners. + +## License [![MIT license][license-img]][license-url] + +>The [`MIT`][license-url] License (MIT) +> +>Copyright © 2014-2021 Sebastian Hildebrandt, [+innovations](http://www.plus-innovations.com). +> +>Permission is hereby granted, free of charge, to any person obtaining a copy +>of this software and associated documentation files (the "Software"), to deal +>in the Software without restriction, including without limitation the rights +>to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +>copies of the Software, and to permit persons to whom the Software is +>furnished to do so, subject to the following conditions: +> +>The above copyright notice and this permission notice shall be included in +>all copies or substantial portions of the Software. +> +>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +>IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +>FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +>AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +>LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +>OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +>THE SOFTWARE. +> +>Further details see [LICENSE](LICENSE) file. + + +[npm-image]: https://img.shields.io/npm/v/systeminformation.svg?style=flat-square +[npm-url]: https://npmjs.org/package/systeminformation +[downloads-image]: https://img.shields.io/npm/dm/systeminformation.svg?style=flat-square +[downloads-url]: https://npmjs.org/package/systeminformation + +[lgtm-badge]: https://img.shields.io/lgtm/grade/javascript/g/sebhildebrandt/systeminformation.svg?style=flat-square +[lgtm-badge-url]: https://lgtm.com/projects/g/sebhildebrandt/systeminformation/context:javascript +[lgtm-alerts]: https://img.shields.io/lgtm/alerts/g/sebhildebrandt/systeminformation.svg?style=flat-square +[lgtm-alerts-url]: https://lgtm.com/projects/g/sebhildebrandt/systeminformation/alerts + +[sponsor-badge]: https://img.shields.io/badge/-Buy%20me%20a%20coffee-blue?style=flat-square +[sponsor-url]: https://www.buymeacoffee.com/systeminfo + +[license-url]: https://github.com/sebhildebrandt/systeminformation/blob/master/LICENSE +[license-img]: https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square +[npmjs-license]: https://img.shields.io/npm/l/systeminformation.svg?style=flat-square +[changelog-url]: https://github.com/sebhildebrandt/systeminformation/blob/master/CHANGELOG.md +[changes5-url]: https://systeminformation.io/changes.html +[caretaker-url]: https://github.com/sebhildebrandt +[caretaker-image]: https://img.shields.io/badge/caretaker-sebhildebrandt-blue.svg?style=flat-square + +[nodejs-url]: https://nodejs.org/en/ +[docker-url]: https://www.docker.com/ +[systeminformation-url]: https://systeminformation.io + +[daviddm-img]: https://img.shields.io/david/sebhildebrandt/systeminformation.svg?style=flat-square +[daviddm-url]: https://david-dm.org/sebhildebrandt/systeminformation + +[issues-img]: https://img.shields.io/github/issues/sebhildebrandt/systeminformation.svg?style=flat-square +[issues-url]: https://github.com/sebhildebrandt/systeminformation/issues +[closed-issues-img]: https://img.shields.io/github/issues-closed-raw/sebhildebrandt/systeminformation.svg?style=flat-square&color=brightgreen +[closed-issues-url]: https://github.com/sebhildebrandt/systeminformation/issues?q=is%3Aissue+is%3Aclosed + +[new-issue]: https://github.com/sebhildebrandt/systeminformation/issues/new/choose + +[mmon-npm-url]: https://npmjs.org/package/mmon +[mmon-github-url]: https://github.com/sebhildebrandt/mmon + +[smc-code-url]: https://github.com/pcafstockf/smc-reader diff --git a/node_modules/systeminformation/lib/audio.js b/node_modules/systeminformation/lib/audio.js new file mode 100644 index 0000000..babebad --- /dev/null +++ b/node_modules/systeminformation/lib/audio.js @@ -0,0 +1,219 @@ +'use strict'; +// @ts-check +// ================================================================================== +// audio.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 16. audio +// ---------------------------------------------------------------------------------- + +const exec = require('child_process').exec; +const execSync = require('child_process').execSync; +const util = require('./util'); +// const fs = require('fs'); + +let _platform = process.platform; + +const _linux = (_platform === 'linux' || _platform === 'android'); +const _darwin = (_platform === 'darwin'); +const _windows = (_platform === 'win32'); +const _freebsd = (_platform === 'freebsd'); +const _openbsd = (_platform === 'openbsd'); +const _netbsd = (_platform === 'netbsd'); +const _sunos = (_platform === 'sunos'); + +function parseAudioType(str, input, output) { + let result = ''; + + if (str.indexOf('speak') >= 0) { result = 'Speaker'; } + if (str.indexOf('laut') >= 0) { result = 'Speaker'; } + if (str.indexOf('loud') >= 0) { result = 'Speaker'; } + if (str.indexOf('head') >= 0) { result = 'Headset'; } + if (str.indexOf('mic') >= 0) { result = 'Microphone'; } + if (str.indexOf('mikr') >= 0) { result = 'Microphone'; } + if (str.indexOf('phone') >= 0) { result = 'Phone'; } + if (str.indexOf('controll') >= 0) { result = 'Controller'; } + if (str.indexOf('line o') >= 0) { result = 'Line Out'; } + if (str.indexOf('digital o') >= 0) { result = 'Digital Out'; } + + if (!result && output) { + result = 'Speaker'; + } else if (!result && input) { + result = 'Microphone'; + } + return result; +} + + +function getLinuxAudioPci() { + let cmd = 'lspci -v 2>/dev/null'; + let result = []; + try { + const parts = execSync(cmd).toString().split('\n\n'); + for (let i = 0; i < parts.length; i++) { + const lines = parts[i].split('\n'); + if (lines && lines.length && lines[0].toLowerCase().indexOf('audio') >= 0) { + const audio = {}; + audio.slotId = lines[0].split(' ')[0]; + audio.driver = util.getValue(lines, 'Kernel driver in use', ':', true) || util.getValue(lines, 'Kernel modules', ':', true); + result.push(audio); + } + } + return result; + } catch (e) { + return result; + } +} + +function parseLinuxAudioPciMM(lines, audioPCI) { + const result = {}; + const slotId = util.getValue(lines, 'Slot'); + + const pciMatch = audioPCI.filter(function (item) { return item.slotId === slotId; }); + + result.id = slotId; + result.name = util.getValue(lines, 'SDevice'); + // result.type = util.getValue(lines, 'Class'); + result.manufacturer = util.getValue(lines, 'SVendor'); + result.revision = util.getValue(lines, 'Rev'); + result.driver = pciMatch && pciMatch.length === 1 && pciMatch[0].driver ? pciMatch[0].driver : ''; + result.default = null; + result.channel = 'PCIe'; + result.type = parseAudioType(result.name, null, null); + result.in = null; + result.out = null; + result.status = 'online'; + + return result; +} + +function parseDarwinChannel(str) { + let result = ''; + + if (str.indexOf('builtin') >= 0) { result = 'Built-In'; } + if (str.indexOf('extern') >= 0) { result = 'Audio-Jack'; } + if (str.indexOf('hdmi') >= 0) { result = 'HDMI'; } + if (str.indexOf('displayport') >= 0) { result = 'Display-Port'; } + if (str.indexOf('usb') >= 0) { result = 'USB'; } + if (str.indexOf('pci') >= 0) { result = 'PCIe'; } + + return result; +} + +function parseDarwinAudio(audioObject, id) { + const result = {}; + const channelStr = ((audioObject.coreaudio_device_transport || '') + ' ' + (audioObject._name || '')).toLowerCase(); + + result.id = id; + result.name = audioObject._name; + result.manufacturer = audioObject.coreaudio_device_manufacturer; + result.revision = null; + result.driver = null; + result.default = !!(audioObject.coreaudio_default_audio_input_device || '') || !!(audioObject.coreaudio_default_audio_output_device || ''); + result.channel = parseDarwinChannel(channelStr); + result.type = parseAudioType(result.name, !!(audioObject.coreaudio_device_input || ''), !!(audioObject.coreaudio_device_output || '')); + result.in = !!(audioObject.coreaudio_device_input || ''); + result.out = !!(audioObject.coreaudio_device_output || ''); + result.status = 'online'; + + return result; +} + +function parseWindowsAudio(lines) { + const result = {}; + const status = util.getValue(lines, 'StatusInfo', ':'); + // const description = util.getValue(lines, 'Description', ':'); + + result.id = util.getValue(lines, 'DeviceID', ':'); // PNPDeviceID?? + result.name = util.getValue(lines, 'name', ':'); + result.manufacturer = util.getValue(lines, 'manufacturer', ':'); + result.revision = null; + result.driver = null; + result.default = null; + result.channel = null; + result.type = parseAudioType(result.name, null, null); + result.in = null; + result.out = null; + result.status = status; + + return result; +} + +function audio(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + let result = []; + if (_linux || _freebsd || _openbsd || _netbsd) { + let cmd = 'lspci -vmm 2>/dev/null'; + exec(cmd, function (error, stdout) { + // PCI + if (!error) { + const audioPCI = getLinuxAudioPci(); + const parts = stdout.toString().split('\n\n'); + for (let i = 0; i < parts.length; i++) { + const lines = parts[i].split('\n'); + if (util.getValue(lines, 'class', ':', true).toLowerCase().indexOf('audio') >= 0) { + const audio = parseLinuxAudioPciMM(lines, audioPCI); + result.push(audio); + } + } + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + if (_darwin) { + let cmd = 'system_profiler SPAudioDataType -json'; + exec(cmd, function (error, stdout) { + if (!error) { + try { + const outObj = JSON.parse(stdout.toString()); + if (outObj.SPAudioDataType && outObj.SPAudioDataType.length && outObj.SPAudioDataType[0] && outObj.SPAudioDataType[0]['_items'] && outObj.SPAudioDataType[0]['_items'].length) { + for (let i = 0; i < outObj.SPAudioDataType[0]['_items'].length; i++) { + const audio = parseDarwinAudio(outObj.SPAudioDataType[0]['_items'][i], i); + result.push(audio); + } + } + } catch (e) { + util.noop(); + } + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + if (_windows) { + util.powerShell('Get-WmiObject Win32_SoundDevice | select DeviceID,StatusInfo,Name,Manufacturer | fl').then((stdout, error) => { + if (!error) { + const parts = stdout.toString().split(/\n\s*\n/); + for (let i = 0; i < parts.length; i++) { + if (util.getValue(parts[i].split('\n'), 'name', ':')) { + result.push(parseWindowsAudio(parts[i].split('\n'))); + } + } + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + if (_sunos) { + resolve(null); + } + }); + }); +} + +exports.audio = audio; diff --git a/node_modules/systeminformation/lib/battery.js b/node_modules/systeminformation/lib/battery.js new file mode 100644 index 0000000..942cb3b --- /dev/null +++ b/node_modules/systeminformation/lib/battery.js @@ -0,0 +1,309 @@ +'use strict'; +// @ts-check; +// ================================================================================== +// battery.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 6. Battery +// ---------------------------------------------------------------------------------- + +const exec = require('child_process').exec; +const fs = require('fs'); +const util = require('./util'); + +let _platform = process.platform; + +const _linux = (_platform === 'linux' || _platform === 'android'); +const _darwin = (_platform === 'darwin'); +const _windows = (_platform === 'win32'); +const _freebsd = (_platform === 'freebsd'); +const _openbsd = (_platform === 'openbsd'); +const _netbsd = (_platform === 'netbsd'); +const _sunos = (_platform === 'sunos'); + +function parseWinBatteryPart(lines, designedCapacity, fullChargeCapacity) { + const result = {}; + let status = util.getValue(lines, 'BatteryStatus', ':').trim(); + // 1 = "Discharging" + // 2 = "On A/C" + // 3 = "Fully Charged" + // 4 = "Low" + // 5 = "Critical" + // 6 = "Charging" + // 7 = "Charging High" + // 8 = "Charging Low" + // 9 = "Charging Critical" + // 10 = "Undefined" + // 11 = "Partially Charged" + if (status >= 0) { + const statusValue = status ? parseInt(status) : 0; + result.status = statusValue; + result.hasBattery = true; + result.maxCapacity = fullChargeCapacity || parseInt(util.getValue(lines, 'DesignCapacity', ':') || 0); + result.designedCapacity = parseInt(util.getValue(lines, 'DesignCapacity', ':') || designedCapacity); + result.voltage = parseInt(util.getValue(lines, 'DesignVoltage', ':') || 0) / 1000.0; + result.capacityUnit = 'mWh'; + result.percent = parseInt(util.getValue(lines, 'EstimatedChargeRemaining', ':') || 0); + result.currentCapacity = parseInt(result.maxCapacity * result.percent / 100); + result.isCharging = (statusValue >= 6 && statusValue <= 9) || statusValue === 11 || (!(statusValue === 3) && !(statusValue === 1) && result.percent < 100); + result.acConnected = result.isCharging || statusValue === 2; + result.model = util.getValue(lines, 'DeviceID', ':'); + } else { + result.status = -1; + } + + return result; +} + +module.exports = function (callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + let result = { + hasBattery: false, + cycleCount: 0, + isCharging: false, + designedCapacity: 0, + maxCapacity: 0, + currentCapacity: 0, + voltage: 0, + capacityUnit: '', + percent: 0, + timeRemaining: null, + acConnected: true, + type: '', + model: '', + manufacturer: '', + serial: '' + }; + + if (_linux) { + let battery_path = ''; + if (fs.existsSync('/sys/class/power_supply/BAT1/uevent')) { + battery_path = '/sys/class/power_supply/BAT1/'; + } else if (fs.existsSync('/sys/class/power_supply/BAT0/uevent')) { + battery_path = '/sys/class/power_supply/BAT0/'; + } + + let acConnected = false; + let acPath = ''; + if (fs.existsSync('/sys/class/power_supply/AC/online')) { + acPath = '/sys/class/power_supply/AC/online'; + } else if (fs.existsSync('/sys/class/power_supply/AC0/online')) { + acPath = '/sys/class/power_supply/AC0/online'; + } + + if (acPath) { + const file = fs.readFileSync(acPath); + acConnected = file.toString().trim() === '1'; + } + + if (battery_path) { + fs.readFile(battery_path + 'uevent', function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + + result.isCharging = (util.getValue(lines, 'POWER_SUPPLY_STATUS', '=').toLowerCase() === 'charging'); + result.acConnected = acConnected || result.isCharging; + result.voltage = parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_VOLTAGE_NOW', '='), 10) / 1000000.0; + result.capacityUnit = result.voltage ? 'mWh' : 'mAh'; + result.cycleCount = parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_CYCLE_COUNT', '='), 10); + result.maxCapacity = Math.round(parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_CHARGE_FULL', '=', true, true), 10) / 1000.0 * (result.voltage || 1)); + const desingedMinVoltage = parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_VOLTAGE_MIN_DESIGN', '='), 10) / 1000000.0; + result.designedCapacity = Math.round(parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_CHARGE_FULL_DESIGN', '=', true, true), 10) / 1000.0 * (desingedMinVoltage || result.voltage || 1)); + result.currentCapacity = Math.round(parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_CHARGE_NOW', '='), 10) / 1000.0 * (result.voltage || 1)); + if (!result.maxCapacity) { + result.maxCapacity = parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_ENERGY_FULL', '=', true, true), 10) / 1000.0; + result.designedCapacity = parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_ENERGY_FULL_DESIGN', '=', true, true), 10) / 1000.0 | result.maxCapacity; + result.currentCapacity = parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_ENERGY_NOW', '='), 10) / 1000.0; + } + const percent = util.getValue(lines, 'POWER_SUPPLY_CAPACITY', '='); + const energy = parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_ENERGY_NOW', '='), 10); + const power = parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_POWER_NOW', '='), 10); + const current = parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_CURRENT_NOW', '='), 10); + + result.percent = parseInt('0' + percent, 10); + if (result.maxCapacity && result.currentCapacity) { + result.hasBattery = true; + if (!percent) { + result.percent = 100.0 * result.currentCapacity / result.maxCapacity; + } + } + if (result.isCharging) { + result.hasBattery = true; + } + if (energy && power) { + result.timeRemaining = Math.floor(energy / power * 60); + } else if (current && result.currentCapacity) { + result.timeRemaining = Math.floor(result.currentCapacity / current * 60); + } + result.type = util.getValue(lines, 'POWER_SUPPLY_TECHNOLOGY', '='); + result.model = util.getValue(lines, 'POWER_SUPPLY_MODEL_NAME', '='); + result.manufacturer = util.getValue(lines, 'POWER_SUPPLY_MANUFACTURER', '='); + result.serial = util.getValue(lines, 'POWER_SUPPLY_SERIAL_NUMBER', '='); + if (callback) { callback(result); } + resolve(result); + } else { + if (callback) { callback(result); } + resolve(result); + } + }); + } else { + if (callback) { callback(result); } + resolve(result); + } + } + if (_freebsd || _openbsd || _netbsd) { + exec('sysctl -i hw.acpi.battery hw.acpi.acline', function (error, stdout) { + let lines = stdout.toString().split('\n'); + const batteries = parseInt('0' + util.getValue(lines, 'hw.acpi.battery.units'), 10); + const percent = parseInt('0' + util.getValue(lines, 'hw.acpi.battery.life'), 10); + result.hasBattery = (batteries > 0); + result.cycleCount = null; + result.isCharging = util.getValue(lines, 'hw.acpi.acline') !== '1'; + result.acConnected = result.isCharging; + result.maxCapacity = null; + result.currentCapacity = null; + result.capacityUnit = 'unknown'; + result.percent = batteries ? percent : null; + if (callback) { callback(result); } + resolve(result); + }); + } + + if (_darwin) { + exec('ioreg -n AppleSmartBattery -r | egrep "CycleCount|IsCharging|DesignCapacity|MaxCapacity|CurrentCapacity|BatterySerialNumber|TimeRemaining|Voltage"; pmset -g batt | grep %', function (error, stdout) { + if (stdout) { + let lines = stdout.toString().replace(/ +/g, '').replace(/"+/g, '').replace(/-/g, '').split('\n'); + result.cycleCount = parseInt('0' + util.getValue(lines, 'cyclecount', '='), 10); + result.voltage = parseInt('0' + util.getValue(lines, 'voltage', '='), 10) / 1000.0; + result.capacityUnit = result.voltage ? 'mWh' : 'mAh'; + result.maxCapacity = Math.round(parseInt('0' + util.getValue(lines, 'applerawmaxcapacity', '='), 10) * (result.voltage || 1)); + result.currentCapacity = Math.round(parseInt('0' + util.getValue(lines, 'applerawcurrentcapacity', '='), 10) * (result.voltage || 1)); + result.designedCapacity = Math.round(parseInt('0' + util.getValue(lines, 'DesignCapacity', '='), 10) * (result.voltage || 1)); + result.manufacturer = 'Apple'; + result.serial = util.getValue(lines, 'BatterySerialNumber', '='); + let percent = null; + const line = util.getValue(lines, 'internal', 'Battery'); + let parts = line.split(';'); + if (parts && parts[0]) { + let parts2 = parts[0].split('\t'); + if (parts2 && parts2[1]) { + percent = parseFloat(parts2[1].trim().replace(/%/g, '')); + } + } + if (parts && parts[1]) { + result.isCharging = (parts[1].trim() === 'charging'); + result.acConnected = (parts[1].trim() !== 'discharging'); + } else { + result.isCharging = util.getValue(lines, 'ischarging', '=').toLowerCase() === 'yes'; + result.acConnected = result.isCharging; + } + if (result.maxCapacity && result.currentCapacity) { + result.hasBattery = true; + result.type = 'Li-ion'; + result.percent = percent !== null ? percent : Math.round(100.0 * result.currentCapacity / result.maxCapacity); + if (!result.isCharging) { + result.timeRemaining = parseInt('0' + util.getValue(lines, 'TimeRemaining', '='), 10); + } + } + } + if (callback) { callback(result); } + resolve(result); + }); + } + if (_sunos) { + if (callback) { callback(result); } + resolve(result); + } + if (_windows) { + try { + const workload = []; + workload.push(util.powerShell('Get-WmiObject Win32_Battery | select BatteryStatus, DesignCapacity, DesignVoltage, EstimatedChargeRemaining, DeviceID | fl')); + workload.push(util.powerShell('(Get-WmiObject -Class BatteryStaticData -Namespace ROOT/WMI).DesignedCapacity')); + workload.push(util.powerShell('(Get-WmiObject -Class BatteryFullChargedCapacity -Namespace ROOT/WMI).FullChargedCapacity')); + util.promiseAll( + workload + ).then(data => { + if (data) { + // let parts = data.results[0].split(/\n\s*\n/); + let parts = data.results[0].split(/\n\s*\n/); + let batteries = []; + const hasValue = value => /\S/.test(value); + for (let i = 0; i < parts.length; i++) { + if (hasValue(parts[i]) && (!batteries.length || !hasValue(parts[i - 1]))) { + batteries.push([]); + } + if (hasValue(parts[i])) { + batteries[batteries.length - 1].push(parts[i]); + } + } + let designCapacities = data.results[1].split('\r\n').filter(e => e); + let fullChargeCapacities = data.results[2].split('\r\n').filter(e => e); + if (batteries.length) { + let first = false; + let additionalBatteries = []; + for (let i = 0; i < batteries.length; i++) { + let lines = batteries[i][0].split('\r\n'); + const designedCapacity = designCapacities && designCapacities.length >= (i + 1) && designCapacities[i] ? util.toInt(designCapacities[i]) : 0; + const fullChargeCapacity = fullChargeCapacities && fullChargeCapacities.length >= (i + 1) && fullChargeCapacities[i] ? util.toInt(fullChargeCapacities[i]) : 0; + const parsed = parseWinBatteryPart(lines, designedCapacity, fullChargeCapacity); + if (!first && parsed.status > 0 && parsed.status !== 10) { + result.hasBattery = parsed.hasBattery; + result.maxCapacity = parsed.maxCapacity; + result.designedCapacity = parsed.designedCapacity; + result.voltage = parsed.voltage; + result.capacityUnit = parsed.capacityUnit; + result.percent = parsed.percent; + result.currentCapacity = parsed.currentCapacity; + result.isCharging = parsed.isCharging; + result.acConnected = parsed.acConnected; + result.model = parsed.model; + first = true; + } else if (parsed.status !== -1) { + additionalBatteries.push( + { + hasBattery: parsed.hasBattery, + maxCapacity: parsed.maxCapacity, + designedCapacity: parsed.designedCapacity, + voltage: parsed.voltage, + capacityUnit: parsed.capacityUnit, + percent: parsed.percent, + currentCapacity: parsed.currentCapacity, + isCharging: parsed.isCharging, + timeRemaining: null, + acConnected: parsed.acConnected, + model: parsed.model, + type: '', + manufacturer: '', + serial: '' + } + ); + } + } + if (!first && additionalBatteries.length) { + result = additionalBatteries[0]; + additionalBatteries.shift(); + } + if (additionalBatteries.length) { + result.additionalBatteries = additionalBatteries; + } + } + } + if (callback) { callback(result); } + resolve(result); + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + }); + }); +}; diff --git a/node_modules/systeminformation/lib/bluetooth.js b/node_modules/systeminformation/lib/bluetooth.js new file mode 100644 index 0000000..76bbe96 --- /dev/null +++ b/node_modules/systeminformation/lib/bluetooth.js @@ -0,0 +1,183 @@ +'use strict'; +// @ts-check +// ================================================================================== +// audio.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 17. bluetooth +// ---------------------------------------------------------------------------------- + +const exec = require('child_process').exec; +const execSync = require('child_process').execSync; +const path = require('path'); +const util = require('./util'); +const fs = require('fs'); + +let _platform = process.platform; + +const _linux = (_platform === 'linux' || _platform === 'android'); +const _darwin = (_platform === 'darwin'); +const _windows = (_platform === 'win32'); +const _freebsd = (_platform === 'freebsd'); +const _openbsd = (_platform === 'openbsd'); +const _netbsd = (_platform === 'netbsd'); +const _sunos = (_platform === 'sunos'); + +function parseBluetoothType(str) { + let result = ''; + + if (str.indexOf('keyboard') >= 0) { result = 'Keyboard'; } + if (str.indexOf('mouse') >= 0) { result = 'Mouse'; } + if (str.indexOf('speaker') >= 0) { result = 'Speaker'; } + if (str.indexOf('headset') >= 0) { result = 'Headset'; } + if (str.indexOf('phone') >= 0) { result = 'Phone'; } + // to be continued ... + + return result; +} + +function parseLinuxBluetoothInfo(lines, macAddr1, macAddr2) { + const result = {}; + + result.device = null; + result.name = util.getValue(lines, 'name', '='); + result.manufacturer = null; + result.macDevice = macAddr1; + result.macHost = macAddr2; + result.batteryPercent = null; + result.type = parseBluetoothType(result.name.toLowerCase()); + result.connected = false; + + return result; +} + +function parseDarwinBluetoothDevices(bluetoothObject, macAddr2) { + const result = {}; + const typeStr = ((bluetoothObject.device_minorClassOfDevice_string || bluetoothObject.device_majorClassOfDevice_string || '') + (bluetoothObject.device_name || '')).toLowerCase(); + + result.device = bluetoothObject.device_services || ''; + result.name = bluetoothObject.device_name || ''; + result.manufacturer = bluetoothObject.device_manufacturer || ''; + result.macDevice = (bluetoothObject.device_addr || '').toLowerCase().replace(/-/g, ':'); + result.macHost = macAddr2; + result.batteryPercent = bluetoothObject.device_batteryPercent || null; + result.type = parseBluetoothType(typeStr); + result.connected = bluetoothObject.device_isconnected === 'attrib_Yes' || false; + + return result; +} + +function parseWindowsBluetooth(lines) { + const result = {}; + + result.device = null; + result.name = util.getValue(lines, 'name', ':'); + result.manufacturer = util.getValue(lines, 'manufacturer', ':'); + result.macDevice = null; + result.macHost = null; + result.batteryPercent = null; + result.type = parseBluetoothType(result.name.toLowerCase()); + result.connected = null; + + return result; +} + +function bluetoothDevices(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + let result = []; + if (_linux) { + // get files in /var/lib/bluetooth/ recursive + const btFiles = util.getFilesInPath('/var/lib/bluetooth/'); + for (let i = 0; i < btFiles.length; i++) { + const filename = path.basename(btFiles[i]); + const pathParts = btFiles[i].split('/'); + const macAddr1 = pathParts.length >= 6 ? pathParts[pathParts.length - 2] : null; + const macAddr2 = pathParts.length >= 7 ? pathParts[pathParts.length - 3] : null; + if (filename === 'info') { + const infoFile = fs.readFileSync(btFiles[i], { encoding: 'utf8' }).split('\n'); + result.push(parseLinuxBluetoothInfo(infoFile, macAddr1, macAddr2)); + } + } + // determine "connected" with hcitool con + try { + const hdicon = execSync('hcitool con').toString().toLowerCase(); + for (let i = 0; i < result.length; i++) { + if (result[i].macDevice && result[i].macDevice.length > 10 && hdicon.indexOf(result[i].macDevice.toLowerCase()) >= 0) { + result[i].connected = true; + } + } + } catch (e) { + util.noop(); + } + + if (callback) { + callback(result); + } + resolve(result); + } + if (_darwin) { + let cmd = 'system_profiler SPBluetoothDataType -json'; + exec(cmd, function (error, stdout) { + if (!error) { + try { + const outObj = JSON.parse(stdout.toString()); + if (outObj.SPBluetoothDataType && outObj.SPBluetoothDataType.length && outObj.SPBluetoothDataType[0] && outObj.SPBluetoothDataType[0]['device_title'] && outObj.SPBluetoothDataType[0]['device_title'].length) { + // missing: host BT Adapter macAddr () + let macAddr2 = null; + if (outObj.SPBluetoothDataType[0]['local_device_title'] && outObj.SPBluetoothDataType[0].local_device_title.general_address) { + macAddr2 = outObj.SPBluetoothDataType[0].local_device_title.general_address.toLowerCase().replace(/-/g, ':'); + } + + for (let i = 0; i < outObj.SPBluetoothDataType[0]['device_title'].length; i++) { + const obj = outObj.SPBluetoothDataType[0]['device_title'][i]; + const objKey = Object.keys(obj); + if (objKey && objKey.length === 1) { + const innerObject = obj[objKey[0]]; + innerObject.device_name = objKey[0]; + const bluetoothDevice = parseDarwinBluetoothDevices(innerObject, macAddr2); + result.push(bluetoothDevice); + } + } + } + } catch (e) { + util.noop(); + } + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + if (_windows) { + util.powerShell('Get-WmiObject Win32_PNPEntity | select PNPClass, Name, Manufacturer | fl').then((stdout, error) => { + if (!error) { + const parts = stdout.toString().split(/\n\s*\n/); + for (let i = 0; i < parts.length; i++) { + if (util.getValue(parts[i].split('\n'), 'PNPClass', ':') === 'Bluetooth') { + result.push(parseWindowsBluetooth(parts[i].split('\n'))); + } + } + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + if (_freebsd || _netbsd || _openbsd || _sunos) { + resolve(null); + } + }); + }); +} + +exports.bluetoothDevices = bluetoothDevices; diff --git a/node_modules/systeminformation/lib/cli.js b/node_modules/systeminformation/lib/cli.js new file mode 100755 index 0000000..3ac1670 --- /dev/null +++ b/node_modules/systeminformation/lib/cli.js @@ -0,0 +1,31 @@ +#!/usr/bin/env node + +'use strict'; +// @ts-check +// ================================================================================== +// cli.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== + +// ---------------------------------------------------------------------------------- +// Dependencies +// ---------------------------------------------------------------------------------- +const si = require('./index'); + +// ---------------------------------------------------------------------------------- +// Main +// ---------------------------------------------------------------------------------- +(function () { + si.getStaticData().then( + (data => { + data.time = si.time(); + console.log(JSON.stringify(data, null, 2)); + } + )); +})(); diff --git a/node_modules/systeminformation/lib/cpu.js b/node_modules/systeminformation/lib/cpu.js new file mode 100644 index 0000000..e50e7a3 --- /dev/null +++ b/node_modules/systeminformation/lib/cpu.js @@ -0,0 +1,1706 @@ +'use strict'; +// @ts-check +// ================================================================================== +// cpu.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 4. CPU +// ---------------------------------------------------------------------------------- + +const os = require('os'); +const exec = require('child_process').exec; +const execSync = require('child_process').execSync; +const fs = require('fs'); +const util = require('./util'); + +let _platform = process.platform; + +const _linux = (_platform === 'linux' || _platform === 'android'); +const _darwin = (_platform === 'darwin'); +const _windows = (_platform === 'win32'); +const _freebsd = (_platform === 'freebsd'); +const _openbsd = (_platform === 'openbsd'); +const _netbsd = (_platform === 'netbsd'); +const _sunos = (_platform === 'sunos'); + +let _cpu_speed = 0; +let _current_cpu = { + user: 0, + nice: 0, + system: 0, + idle: 0, + irq: 0, + load: 0, + tick: 0, + ms: 0, + currentLoad: 0, + currentLoadUser: 0, + currentLoadSystem: 0, + currentLoadNice: 0, + currentLoadIdle: 0, + currentLoadIrq: 0, + rawCurrentLoad: 0, + rawCurrentLoadUser: 0, + rawCurrentLoadSystem: 0, + rawCurrentLoadNice: 0, + rawCurrentLoadIdle: 0, + rawCurrentLoadIrq: 0 +}; +let _cpus = []; +let _corecount = 0; + +const AMDBaseFrequencies = { + '8346': '1.8', + '8347': '1.9', + '8350': '2.0', + '8354': '2.2', + '8356|SE': '2.4', + '8356': '2.3', + '8360': '2.5', + '2372': '2.1', + '2373': '2.1', + '2374': '2.2', + '2376': '2.3', + '2377': '2.3', + '2378': '2.4', + '2379': '2.4', + '2380': '2.5', + '2381': '2.5', + '2382': '2.6', + '2384': '2.7', + '2386': '2.8', + '2387': '2.8', + '2389': '2.9', + '2393': '3.1', + '8374': '2.2', + '8376': '2.3', + '8378': '2.4', + '8379': '2.4', + '8380': '2.5', + '8381': '2.5', + '8382': '2.6', + '8384': '2.7', + '8386': '2.8', + '8387': '2.8', + '8389': '2.9', + '8393': '3.1', + '2419EE': '1.8', + '2423HE': '2.0', + '2425HE': '2.1', + '2427': '2.2', + '2431': '2.4', + '2435': '2.6', + '2439SE': '2.8', + '8425HE': '2.1', + '8431': '2.4', + '8435': '2.6', + '8439SE': '2.8', + '4122': '2.2', + '4130': '2.6', + '4162EE': '1.7', + '4164EE': '1.8', + '4170HE': '2.1', + '4174HE': '2.3', + '4176HE': '2.4', + '4180': '2.6', + '4184': '2.8', + '6124HE': '1.8', + '6128HE': '2.0', + '6132HE': '2.2', + '6128': '2.0', + '6134': '2.3', + '6136': '2.4', + '6140': '2.6', + '6164HE': '1.7', + '6166HE': '1.8', + '6168': '1.9', + '6172': '2.1', + '6174': '2.2', + '6176': '2.3', + '6176SE': '2.3', + '6180SE': '2.5', + '3250': '2.5', + '3260': '2.7', + '3280': '2.4', + '4226': '2.7', + '4228': '2.8', + '4230': '2.9', + '4234': '3.1', + '4238': '3.3', + '4240': '3.4', + '4256': '1.6', + '4274': '2.5', + '4276': '2.6', + '4280': '2.8', + '4284': '3.0', + '6204': '3.3', + '6212': '2.6', + '6220': '3.0', + '6234': '2.4', + '6238': '2.6', + '6262HE': '1.6', + '6272': '2.1', + '6274': '2.2', + '6276': '2.3', + '6278': '2.4', + '6282SE': '2.6', + '6284SE': '2.7', + '6308': '3.5', + '6320': '2.8', + '6328': '3.2', + '6338P': '2.3', + '6344': '2.6', + '6348': '2.8', + '6366': '1.8', + '6370P': '2.0', + '6376': '2.3', + '6378': '2.4', + '6380': '2.5', + '6386': '2.8', + 'FX|4100': '3.6', + 'FX|4120': '3.9', + 'FX|4130': '3.8', + 'FX|4150': '3.8', + 'FX|4170': '4.2', + 'FX|6100': '3.3', + 'FX|6120': '3.6', + 'FX|6130': '3.6', + 'FX|6200': '3.8', + 'FX|8100': '2.8', + 'FX|8120': '3.1', + 'FX|8140': '3.2', + 'FX|8150': '3.6', + 'FX|8170': '3.9', + 'FX|4300': '3.8', + 'FX|4320': '4.0', + 'FX|4350': '4.2', + 'FX|6300': '3.5', + 'FX|6350': '3.9', + 'FX|8300': '3.3', + 'FX|8310': '3.4', + 'FX|8320': '3.5', + 'FX|8350': '4.0', + 'FX|8370': '4.0', + 'FX|9370': '4.4', + 'FX|9590': '4.7', + 'FX|8320E': '3.2', + 'FX|8370E': '3.3', + + // ZEN Desktop CPUs + '1200': '3.1', + 'Pro 1200': '3.1', + '1300X': '3.5', + 'Pro 1300': '3.5', + '1400': '3.2', + '1500X': '3.5', + 'Pro 1500': '3.5', + '1600': '3.2', + '1600X': '3.6', + 'Pro 1600': '3.2', + '1700': '3.0', + 'Pro 1700': '3.0', + '1700X': '3.4', + 'Pro 1700X': '3.4', + '1800X': '3.6', + '1900X': '3.8', + '1920': '3.2', + '1920X': '3.5', + '1950X': '3.4', + + // ZEN Desktop APUs + '200GE': '3.2', + 'Pro 200GE': '3.2', + '220GE': '3.4', + '240GE': '3.5', + '3000G': '3.5', + '300GE': '3.4', + '3050GE': '3.4', + '2200G': '3.5', + 'Pro 2200G': '3.5', + '2200GE': '3.2', + 'Pro 2200GE': '3.2', + '2400G': '3.6', + 'Pro 2400G': '3.6', + '2400GE': '3.2', + 'Pro 2400GE': '3.2', + + // ZEN Mobile APUs + 'Pro 200U': '2.3', + '300U': '2.4', + '2200U': '2.5', + '3200U': '2.6', + '2300U': '2.0', + 'Pro 2300U': '2.0', + '2500U': '2.0', + 'Pro 2500U': '2.2', + '2600H': '3.2', + '2700U': '2.0', + 'Pro 2700U': '2.2', + '2800H': '3.3', + + // ZEN Server Processors + '7351': '2.4', + '7351P': '2.4', + '7401': '2.0', + '7401P': '2.0', + '7551P': '2.0', + '7551': '2.0', + '7251': '2.1', + '7261': '2.5', + '7281': '2.1', + '7301': '2.2', + '7371': '3.1', + '7451': '2.3', + '7501': '2.0', + '7571': '2.2', + '7601': '2.2', + + // ZEN Embedded Processors + 'V1500B': '2.2', + 'V1780B': '3.35', + 'V1202B': '2.3', + 'V1404I': '2.0', + 'V1605B': '2.0', + 'V1756B': '3.25', + 'V1807B': '3.35', + + '3101': '2.1', + '3151': '2.7', + '3201': '1.5', + '3251': '2.5', + '3255': '2.5', + '3301': '2.0', + '3351': '1.9', + '3401': '1.85', + '3451': '2.15', + + // ZEN+ Desktop + '1200|AF': '3.1', + '2300X': '3.5', + '2500X': '3.6', + '2600': '3.4', + '2600E': '3.1', + '1600|AF': '3.2', + '2600X': '3.6', + '2700': '3.2', + '2700E': '2.8', + 'Pro 2700': '3.2', + '2700X': '3.7', + 'Pro 2700X': '3.6', + '2920X': '3.5', + '2950X': '3.5', + '2970WX': '3.0', + '2990WX': '3.0', + + // ZEN+ Desktop APU + 'Pro 300GE': '3.4', + 'Pro 3125GE': '3.4', + '3150G': '3.5', + 'Pro 3150G': '3.5', + '3150GE': '3.3', + 'Pro 3150GE': '3.3', + '3200G': '3.6', + 'Pro 3200G': '3.6', + '3200GE': '3.3', + 'Pro 3200GE': '3.3', + '3350G': '3.6', + 'Pro 3350G': '3.6', + '3350GE': '3.3', + 'Pro 3350GE': '3.3', + '3400G': '3.7', + 'Pro 3400G': '3.7', + '3400GE': '3.3', + 'Pro 3400GE': '3.3', + + // ZEN+ Mobile + '3300U': '2.1', + 'PRO 3300U': '2.1', + '3450U': '2.1', + '3500U': '2.1', + 'PRO 3500U': '2.1', + '3500C': '2.1', + '3550H': '2.1', + '3580U': '2.1', + '3700U': '2.3', + 'PRO 3700U': '2.3', + '3700C': '2.3', + '3750H': '2.3', + '3780U': '2.3', + + // ZEN2 Desktop CPUS + '3100': '3.6', + '3300X': '3.8', + '3500': '3.6', + '3500X': '3.6', + '3600': '3.6', + 'Pro 3600': '3.6', + '3600X': '3.8', + '3600XT': '3.8', + 'Pro 3700': '3.6', + '3700X': '3.6', + '3800X': '3.9', + '3800XT': '3.9', + '3900': '3.1', + 'Pro 3900': '3.1', + '3900X': '3.8', + '3900XT': '3.8', + '3950X': '3.5', + '3960X': '3.8', + '3970X': '3.7', + '3990X': '2.9', + '3945WX': '4.0', + '3955WX': '3.9', + '3975WX': '3.5', + '3995WX': '2.7', + + // ZEN2 Desktop APUs + '4300GE': '3.5', + 'Pro 4300GE': '3.5', + '4300G': '3.8', + 'Pro 4300G': '3.8', + '4600GE': '3.3', + 'Pro 4650GE': '3.3', + '4600G': '3.7', + 'Pro 4650G': '3.7', + '4700GE': '3.1', + 'Pro 4750GE': '3.1', + '4700G': '3.6', + 'Pro 4750G': '3.6', + '4300U': '2.7', + '4450U': '2.5', + 'Pro 4450U': '2.5', + '4500U': '2.3', + '4600U': '2.1', + 'PRO 4650U': '2.1', + '4680U': '2.1', + '4600HS': '3.0', + '4600H': '3.0', + '4700U': '2.0', + 'PRO 4750U': '1.7', + '4800U': '1.8', + '4800HS': '2.9', + '4800H': '2.9', + '4900HS': '3.0', + '4900H': '3.3', + '5300U': '2.6', + '5500U': '2.1', + '5700U': '1.8', + + // ZEN2 - EPYC + '7232P': '3.1', + '7302P': '3.0', + '7402P': '2.8', + '7502P': '2.5', + '7702P': '2.0', + '7252': '3.1', + '7262': '3.2', + '7272': '2.9', + '7282': '2.8', + '7302': '3.0', + '7352': '2.3', + '7402': '2.8', + '7452': '2.35', + '7502': '2.5', + '7532': '2.4', + '7542': '2.9', + '7552': '2.2', + '7642': '2.3', + '7662': '2.0', + '7702': '2.0', + '7742': '2.25', + '7H12': '2.6', + '7F32': '3.7', + '7F52': '3.5', + '7F72': '3.2', + + // Epyc (Milan) + + '7763': '2.45', + '7713': '2.0', + '7713P': '2.0', + '7663': '2.0', + '7643': '2.3', + '75F3': '2.95', + '7543': '2.8', + '7543P': '2.8', + '7513': '2.6', + '7453': '2.75', + '74F3': '3.2', + '7443': '2.85', + '7443P': '2.85', + '7413': '2.65', + '73F3': '3.5', + '7343': '3.2', + '7313': '3.0', + '7313P': '3.0', + '72F3': '3.7', + + // ZEN3 + '5600X': '3.7', + '5800X': '3.8', + '5900X': '3.7', + '5950X': '3.4' +}; + + +const socketTypes = { + 1: 'Other', + 2: 'Unknown', + 3: 'Daughter Board', + 4: 'ZIF Socket', + 5: 'Replacement/Piggy Back', + 6: 'None', + 7: 'LIF Socket', + 8: 'Slot 1', + 9: 'Slot 2', + 10: '370 Pin Socket', + 11: 'Slot A', + 12: 'Slot M', + 13: '423', + 14: 'A (Socket 462)', + 15: '478', + 16: '754', + 17: '940', + 18: '939', + 19: 'mPGA604', + 20: 'LGA771', + 21: 'LGA775', + 22: 'S1', + 23: 'AM2', + 24: 'F (1207)', + 25: 'LGA1366', + 26: 'G34', + 27: 'AM3', + 28: 'C32', + 29: 'LGA1156', + 30: 'LGA1567', + 31: 'PGA988A', + 32: 'BGA1288', + 33: 'rPGA988B', + 34: 'BGA1023', + 35: 'BGA1224', + 36: 'LGA1155', + 37: 'LGA1356', + 38: 'LGA2011', + 39: 'FS1', + 40: 'FS2', + 41: 'FM1', + 42: 'FM2', + 43: 'LGA2011-3', + 44: 'LGA1356-3', + 45: 'LGA1150', + 46: 'BGA1168', + 47: 'BGA1234', + 48: 'BGA1364', + 49: 'AM4', + 50: 'LGA1151', + 51: 'BGA1356', + 52: 'BGA1440', + 53: 'BGA1515', + 54: 'LGA3647-1', + 55: 'SP3', + 56: 'SP3r2', + 57: 'LGA2066', + 58: 'BGA1392', + 59: 'BGA1510', + 60: 'BGA1528', + 61: 'LGA4189', + 62: 'LGA1200', + 63: 'LGA4677', +}; + +const socketTypesByName = { + 'LGA1150': 'i7-5775C i3-4340 i3-4170 G3250 i3-4160T i3-4160 E3-1231 G3258 G3240 i7-4790S i7-4790K i7-4790 i5-4690K i5-4690 i5-4590T i5-4590S i5-4590 i5-4460 i3-4360 i3-4150 G1820 G3420 G3220 i7-4771 i5-4440 i3-4330 i3-4130T i3-4130 E3-1230 i7-4770S i7-4770K i7-4770 i5-4670K i5-4670 i5-4570T i5-4570S i5-4570 i5-4430', + 'LGA1151': 'i9-9900KS E-2288G E-2224 G5420 i9-9900T i9-9900 i7-9700T i7-9700F i7-9700E i7-9700 i5-9600 i5-9500T i5-9500F i5-9500 i5-9400T i3-9350K i3-9300 i3-9100T i3-9100F i3-9100 G4930 i9-9900KF i7-9700KF i5-9600KF i5-9400F i5-9400 i3-9350KF i9-9900K i7-9700K i5-9600K G5500 G5400 i7-8700T i7-8086K i5-8600 i5-8500T i5-8500 i5-8400T i3-8300 i3-8100T G4900 i7-8700K i7-8700 i5-8600K i5-8400 i3-8350K i3-8100 E3-1270 G4600 G4560 i7-7700T i7-7700K i7-7700 i5-7600K i5-7600 i5-7500T i5-7500 i5-7400 i3-7350K i3-7300 i3-7100T i3-7100 G3930 G3900 G4400 i7-6700T i7-6700K i7-6700 i5-6600K i5-6600 i5-6500T i5-6500 i5-6400T i5-6400 i3-6300 i3-6100T i3-6100 E3-1270 E3-1270 T4500 T4400', + '1155': 'G440 G460 G465 G470 G530T G540T G550T G1610T G1620T G530 G540 G1610 G550 G1620 G555 G1630 i3-2100T i3-2120T i3-3220T i3-3240T i3-3250T i3-2100 i3-2105 i3-2102 i3-3210 i3-3220 i3-2125 i3-2120 i3-3225 i3-2130 i3-3245 i3-3240 i3-3250 i5-3570T i5-2500T i5-2400S i5-2405S i5-2390T i5-3330S i5-2500S i5-3335S i5-2300 i5-3450S i5-3340S i5-3470S i5-3475S i5-3470T i5-2310 i5-3550S i5-2320 i5-3330 i5-3350P i5-3450 i5-2400 i5-3340 i5-3570S i5-2380P i5-2450P i5-3470 i5-2500K i5-3550 i5-2500 i5-3570 i5-3570K i5-2550K i7-3770T i7-2600S i7-3770S i7-2600K i7-2600 i7-3770 i7-3770K i7-2700K G620T G630T G640T G2020T G645T G2100T G2030T G622 G860T G620 G632 G2120T G630 G640 G2010 G840 G2020 G850 G645 G2030 G860 G2120 G870 G2130 G2140 E3-1220L E3-1220L E3-1260L E3-1265L E3-1220 E3-1225 E3-1220 E3-1235 E3-1225 E3-1230 E3-1230 E3-1240 E3-1245 E3-1270 E3-1275 E3-1240 E3-1245 E3-1270 E3-1280 E3-1275 E3-1290 E3-1280 E3-1290' +}; + +function getSocketTypesByName(str) { + let result = ''; + for (const key in socketTypesByName) { + const names = socketTypesByName[key].split(' '); + for (let i = 0; i < names.length; i++) { + if (str.indexOf(names[i]) >= 0) { + result = key; + } + } + } + return result; +} + +function cpuManufacturer(str) { + let result = str; + str = str.toLowerCase(); + + if (str.indexOf('intel') >= 0) { result = 'Intel'; } + if (str.indexOf('amd') >= 0) { result = 'AMD'; } + if (str.indexOf('qemu') >= 0) { result = 'QEMU'; } + if (str.indexOf('hygon') >= 0) { result = 'Hygon'; } + if (str.indexOf('centaur') >= 0) { result = 'WinChip/Via'; } + if (str.indexOf('vmware') >= 0) { result = 'VMware'; } + if (str.indexOf('Xen') >= 0) { result = 'Xen Hypervisor'; } + if (str.indexOf('tcg') >= 0) { result = 'QEMU'; } + if (str.indexOf('apple') >= 0) { result = 'Apple'; } + + return result; +} + +function cpuBrandManufacturer(res) { + res.brand = res.brand.replace(/\(R\)+/g, '®').replace(/\s+/g, ' ').trim(); + res.brand = res.brand.replace(/\(TM\)+/g, '™').replace(/\s+/g, ' ').trim(); + res.brand = res.brand.replace(/\(C\)+/g, '©').replace(/\s+/g, ' ').trim(); + res.brand = res.brand.replace(/CPU+/g, '').replace(/\s+/g, ' ').trim(); + res.manufacturer = cpuManufacturer(res.brand); + + let parts = res.brand.split(' '); + parts.shift(); + res.brand = parts.join(' '); + return res; +} + +function getAMDSpeed(brand) { + let result = '0'; + for (let key in AMDBaseFrequencies) { + if ({}.hasOwnProperty.call(AMDBaseFrequencies, key)) { + let parts = key.split('|'); + let found = 0; + parts.forEach(item => { + if (brand.indexOf(item) > -1) { + found++; + } + }); + if (found === parts.length) { + result = AMDBaseFrequencies[key]; + } + } + } + return parseFloat(result); +} + +// -------------------------- +// CPU - brand, speed + +function getCpu() { + + return new Promise((resolve) => { + process.nextTick(() => { + const UNKNOWN = 'unknown'; + let result = { + manufacturer: UNKNOWN, + brand: UNKNOWN, + vendor: '', + family: '', + model: '', + stepping: '', + revision: '', + voltage: '', + speed: 0, + speedMin: 0, + speedMax: 0, + governor: '', + cores: util.cores(), + physicalCores: util.cores(), + processors: 1, + socket: '', + flags: '', + virtualization: false, + cache: {} + }; + cpuFlags().then(flags => { + result.flags = flags; + result.virtualization = flags.indexOf('vmx') > -1 || flags.indexOf('svm') > -1; + // if (_windows) { + // try { + // const systeminfo = execSync('systeminfo', util.execOptsWin).toString(); + // result.virtualization = result.virtualization || (systeminfo.indexOf('Virtualization Enabled In Firmware: Yes') !== -1) || (systeminfo.indexOf('Virtualisierung in Firmware aktiviert: Ja') !== -1) || (systeminfo.indexOf('Virtualisation activée dans le microprogramme : Qiu') !== -1); + // } catch (e) { + // util.noop(); + // } + // } + if (_darwin) { + exec('sysctl machdep.cpu hw.cpufrequency_max hw.cpufrequency_min hw.packages hw.physicalcpu_max hw.ncpu hw.tbfrequency hw.cpufamily hw.cpusubfamily', function (error, stdout) { + let lines = stdout.toString().split('\n'); + const modelline = util.getValue(lines, 'machdep.cpu.brand_string'); + const modellineParts = modelline.split('@'); + result.brand = modellineParts[0].trim(); + const speed = modellineParts[1] ? modellineParts[1].trim() : '0'; + result.speed = parseFloat(speed.replace(/GHz+/g, '')); + let tbFrequency = util.getValue(lines, 'hw.tbfrequency') / 1000000000.0; + tbFrequency = tbFrequency < 0.1 ? tbFrequency * 100 : tbFrequency; + result.speed = result.speed === 0 ? tbFrequency : result.speed; + + _cpu_speed = result.speed; + result = cpuBrandManufacturer(result); + result.speedMin = util.getValue(lines, 'hw.cpufrequency_min') ? (util.getValue(lines, 'hw.cpufrequency_min') / 1000000000.0) : result.speed; + result.speedMax = util.getValue(lines, 'hw.cpufrequency_max') ? (util.getValue(lines, 'hw.cpufrequency_max') / 1000000000.0) : result.speed; + result.vendor = util.getValue(lines, 'machdep.cpu.vendor') || 'Apple'; + result.family = util.getValue(lines, 'machdep.cpu.family') || util.getValue(lines, 'hw.cpufamily'); + result.model = util.getValue(lines, 'machdep.cpu.model'); + result.stepping = util.getValue(lines, 'machdep.cpu.stepping') || util.getValue(lines, 'hw.cpusubfamily'); + const countProcessors = util.getValue(lines, 'hw.packages'); + const countCores = util.getValue(lines, 'hw.physicalcpu_max'); + const countThreads = util.getValue(lines, 'hw.ncpu'); + if (os.arch() === 'arm64') { + const clusters = execSync('ioreg -c IOPlatformDevice -d 3 -r | grep cluster-type').toString().split('\n'); + const efficiencyCores = clusters.filter(line => line.indexOf('"E"') >= 0).length; + const performanceCores = clusters.filter(line => line.indexOf('"P"') >= 0).length; + result.socket = 'SOC'; + result.efficiencyCores = efficiencyCores; + result.performanceCores = performanceCores; + } + if (countProcessors) { + result.processors = parseInt(countProcessors) || 1; + } + if (countCores && countThreads) { + result.cores = parseInt(countThreads) || util.cores(); + result.physicalCores = parseInt(countCores) || util.cores(); + } + cpuCache().then(res => { + result.cache = res; + resolve(result); + }); + }); + } + if (_linux) { + let modelline = ''; + let lines = []; + if (os.cpus()[0] && os.cpus()[0].model) { modelline = os.cpus()[0].model; } + exec('export LC_ALL=C; lscpu; echo -n "Governor: "; cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 2>/dev/null; echo; unset LC_ALL', function (error, stdout) { + if (!error) { + lines = stdout.toString().split('\n'); + } + modelline = util.getValue(lines, 'model name') || modelline; + const modellineParts = modelline.split('@'); + result.brand = modellineParts[0].trim(); + result.speed = modellineParts[1] ? parseFloat(modellineParts[1].trim()) : 0; + if (result.speed === 0 && (result.brand.indexOf('AMD') > -1 || result.brand.toLowerCase().indexOf('ryzen') > -1)) { + result.speed = getAMDSpeed(result.brand); + } + if (result.speed === 0) { + const current = getCpuCurrentSpeedSync(); + if (current.avg !== 0) { result.speed = current.avg; } + } + _cpu_speed = result.speed; + result.speedMin = Math.round(parseFloat(util.getValue(lines, 'cpu min mhz').replace(/,/g, '.')) / 10.0) / 100; + result.speedMax = Math.round(parseFloat(util.getValue(lines, 'cpu max mhz').replace(/,/g, '.')) / 10.0) / 100; + + result = cpuBrandManufacturer(result); + result.vendor = cpuManufacturer(util.getValue(lines, 'vendor id')); + // if (!result.vendor) { result.vendor = util.getValue(lines, 'anbieterkennung'); } + + result.family = util.getValue(lines, 'cpu family'); + // if (!result.family) { result.family = util.getValue(lines, 'prozessorfamilie'); } + result.model = util.getValue(lines, 'model:'); + // if (!result.model) { result.model = util.getValue(lines, 'modell:'); } + result.stepping = util.getValue(lines, 'stepping'); + result.revision = util.getValue(lines, 'cpu revision'); + result.cache.l1d = util.getValue(lines, 'l1d cache'); + if (result.cache.l1d) { result.cache.l1d = parseInt(result.cache.l1d) * (result.cache.l1d.indexOf('M') !== -1 ? 1024 * 1024 : (result.cache.l1d.indexOf('K') !== -1 ? 1024 : 1)); } + result.cache.l1i = util.getValue(lines, 'l1i cache'); + if (result.cache.l1i) { result.cache.l1i = parseInt(result.cache.l1i) * (result.cache.l1i.indexOf('M') !== -1 ? 1024 * 1024 : (result.cache.l1i.indexOf('K') !== -1 ? 1024 : 1)); } + result.cache.l2 = util.getValue(lines, 'l2 cache'); + if (result.cache.l2) { result.cache.l2 = parseInt(result.cache.l2) * (result.cache.l2.indexOf('M') !== -1 ? 1024 * 1024 : (result.cache.l2.indexOf('K') !== -1 ? 1024 : 1)); } + result.cache.l3 = util.getValue(lines, 'l3 cache'); + if (result.cache.l3) { result.cache.l3 = parseInt(result.cache.l3) * (result.cache.l3.indexOf('M') !== -1 ? 1024 * 1024 : (result.cache.l3.indexOf('K') !== -1 ? 1024 : 1)); } + + const threadsPerCore = util.getValue(lines, 'thread(s) per core') || '1'; + // const coresPerSocketInt = parseInt(util.getValue(lines, 'cores(s) per socket') || '1', 10); + const processors = util.getValue(lines, 'socket(s)') || '1'; + let threadsPerCoreInt = parseInt(threadsPerCore, 10); + let processorsInt = parseInt(processors, 10); + result.physicalCores = result.cores / threadsPerCoreInt; + result.processors = processorsInt; + result.governor = util.getValue(lines, 'governor') || ''; + + // Test Raspberry + if (result.vendor === 'ARM') { + const linesRpi = fs.readFileSync('/proc/cpuinfo').toString().split('\n'); + const rPIRevision = util.decodePiCpuinfo(linesRpi); + if (rPIRevision.model.toLowerCase().indexOf('raspberry') >= 0) { + result.family = result.manufacturer; + result.manufacturer = rPIRevision.manufacturer; + result.brand = rPIRevision.processor; + result.revision = rPIRevision.revisionCode; + result.socket = 'SOC'; + } + } + + // socket type + let lines2 = []; + exec('export LC_ALL=C; dmidecode –t 4 2>/dev/null | grep "Upgrade: Socket"; unset LC_ALL', function (error2, stdout2) { + lines2 = stdout2.toString().split('\n'); + if (lines2 && lines2.length) { + result.socket = util.getValue(lines2, 'Upgrade').replace('Socket', '').trim() || result.socket; + } + resolve(result); + }); + }); + } + if (_freebsd || _openbsd || _netbsd) { + let modelline = ''; + let lines = []; + if (os.cpus()[0] && os.cpus()[0].model) { modelline = os.cpus()[0].model; } + exec('export LC_ALL=C; dmidecode -t 4; dmidecode -t 7 unset LC_ALL', function (error, stdout) { + let cache = []; + if (!error) { + const data = stdout.toString().split('# dmidecode'); + const processor = data.length > 1 ? data[1] : ''; + cache = data.length > 2 ? data[2].split('Cache Information') : []; + + lines = processor.split('\n'); + } + result.brand = modelline.split('@')[0].trim(); + result.speed = modelline.split('@')[1] ? parseFloat(modelline.split('@')[1].trim()) : 0; + if (result.speed === 0 && (result.brand.indexOf('AMD') > -1 || result.brand.toLowerCase().indexOf('ryzen') > -1)) { + result.speed = getAMDSpeed(result.brand); + } + if (result.speed === 0) { + const current = getCpuCurrentSpeedSync(); + if (current.avg !== 0) { result.speed = current.avg; } + } + _cpu_speed = result.speed; + result.speedMin = result.speed; + result.speedMax = Math.round(parseFloat(util.getValue(lines, 'max speed').replace(/Mhz/g, '')) / 10.0) / 100; + + result = cpuBrandManufacturer(result); + result.vendor = cpuManufacturer(util.getValue(lines, 'manufacturer')); + let sig = util.getValue(lines, 'signature'); + sig = sig.split(','); + for (var i = 0; i < sig.length; i++) { + sig[i] = sig[i].trim(); + } + result.family = util.getValue(sig, 'Family', ' ', true); + result.model = util.getValue(sig, 'Model', ' ', true); + result.stepping = util.getValue(sig, 'Stepping', ' ', true); + result.revision = ''; + const voltage = parseFloat(util.getValue(lines, 'voltage')); + result.voltage = isNaN(voltage) ? '' : voltage.toFixed(2); + for (let i = 0; i < cache.length; i++) { + lines = cache[i].split('\n'); + let cacheType = util.getValue(lines, 'Socket Designation').toLowerCase().replace(' ', '-').split('-'); + cacheType = cacheType.length ? cacheType[0] : ''; + const sizeParts = util.getValue(lines, 'Installed Size').split(' '); + let size = parseInt(sizeParts[0], 10); + const unit = sizeParts.length > 1 ? sizeParts[1] : 'kb'; + size = size * (unit === 'kb' ? 1024 : (unit === 'mb' ? 1024 * 1024 : (unit === 'gb' ? 1024 * 1024 * 1024 : 1))); + if (cacheType) { + if (cacheType === 'l1') { + result.cache[cacheType + 'd'] = size / 2; + result.cache[cacheType + 'i'] = size / 2; + } else { + result.cache[cacheType] = size; + } + } + } + // socket type + result.socket = util.getValue(lines, 'Upgrade').replace('Socket', '').trim(); + // # threads / # cores + const threadCount = util.getValue(lines, 'thread count').trim(); + const coreCount = util.getValue(lines, 'core count').trim(); + if (coreCount && threadCount) { + result.cores = parseInt(threadCount, 10); + result.physicalCores = parseInt(coreCount, 10); + } + resolve(result); + }); + } + if (_sunos) { + resolve(result); + } + if (_windows) { + try { + const workload = []; + workload.push(util.powerShell('Get-WmiObject Win32_processor | select Name, Revision, L2CacheSize, L3CacheSize, Manufacturer, MaxClockSpeed, Description, UpgradeMethod, Caption, NumberOfLogicalProcessors, NumberOfCores | fl')); + workload.push(util.powerShell('Get-WmiObject Win32_CacheMemory | select CacheType,InstalledSize,Level | fl')); + // workload.push(util.powerShell('Get-ComputerInfo -property "HyperV*"')); + workload.push(util.powerShell('(Get-CimInstance Win32_ComputerSystem).HypervisorPresent')); + + Promise.all( + workload + ).then(data => { + let lines = data[0].split('\r\n'); + let name = util.getValue(lines, 'name', ':') || ''; + if (name.indexOf('@') >= 0) { + result.brand = name.split('@')[0].trim(); + result.speed = name.split('@')[1] ? parseFloat(name.split('@')[1].trim()) : 0; + _cpu_speed = result.speed; + } else { + result.brand = name.trim(); + result.speed = 0; + } + result = cpuBrandManufacturer(result); + result.revision = util.getValue(lines, 'revision', ':'); + result.cache.l1d = 0; + result.cache.l1i = 0; + result.cache.l2 = util.getValue(lines, 'l2cachesize', ':'); + result.cache.l3 = util.getValue(lines, 'l3cachesize', ':'); + if (result.cache.l2) { result.cache.l2 = parseInt(result.cache.l2, 10) * 1024; } + if (result.cache.l3) { result.cache.l3 = parseInt(result.cache.l3, 10) * 1024; } + result.vendor = util.getValue(lines, 'manufacturer', ':'); + result.speedMax = Math.round(parseFloat(util.getValue(lines, 'maxclockspeed', ':').replace(/,/g, '.')) / 10.0) / 100; + if (result.speed === 0 && (result.brand.indexOf('AMD') > -1 || result.brand.toLowerCase().indexOf('ryzen') > -1)) { + result.speed = getAMDSpeed(result.brand); + } + if (result.speed === 0) { + result.speed = result.speedMax; + } + result.speedMin = result.speed; + + let description = util.getValue(lines, 'description', ':').split(' '); + for (let i = 0; i < description.length; i++) { + if (description[i].toLowerCase().startsWith('family') && (i + 1) < description.length && description[i + 1]) { + result.family = description[i + 1]; + } + if (description[i].toLowerCase().startsWith('model') && (i + 1) < description.length && description[i + 1]) { + result.model = description[i + 1]; + } + if (description[i].toLowerCase().startsWith('stepping') && (i + 1) < description.length && description[i + 1]) { + result.stepping = description[i + 1]; + } + } + // socket type + const socketId = util.getValue(lines, 'UpgradeMethod', ':'); + if (socketTypes[socketId]) { + result.socket = socketTypes[socketId]; + } + const socketByName = getSocketTypesByName(name); + if (socketByName) { + result.socket = socketByName; + } + // # threads / # cores + const countProcessors = util.countLines(lines, 'Caption'); + const countThreads = util.getValue(lines, 'NumberOfLogicalProcessors', ':'); + const countCores = util.getValue(lines, 'NumberOfCores', ':'); + if (countProcessors) { + result.processors = parseInt(countProcessors) || 1; + } + if (countCores && countThreads) { + result.cores = parseInt(countThreads) || util.cores(); + result.physicalCores = parseInt(countCores) || util.cores(); + } + if (countProcessors > 1) { + result.cores = result.cores * countProcessors; + result.physicalCores = result.physicalCores * countProcessors; + } + const parts = data[1].split(/\n\s*\n/); + parts.forEach(function (part) { + lines = part.split('\r\n'); + const cacheType = util.getValue(lines, 'CacheType'); + const level = util.getValue(lines, 'Level'); + const installedSize = util.getValue(lines, 'InstalledSize'); + // L1 Instructions + if (level === '3' && cacheType === '3') { + result.cache.l1i = parseInt(installedSize, 10); + } + // L1 Data + if (level === '3' && cacheType === '4') { + result.cache.l1d = parseInt(installedSize, 10); + } + // L1 all + if (level === '3' && cacheType === '5' && !result.cache.l1i && !result.cache.l1d) { + result.cache.l1i = parseInt(installedSize, 10) / 2; + result.cache.l1d = parseInt(installedSize, 10) / 2; + } + }); + // lines = data[2].split('\r\n'); + // result.virtualization = (util.getValue(lines, 'HyperVRequirementVirtualizationFirmwareEnabled').toLowerCase() === 'true'); + // result.virtualization = (util.getValue(lines, 'HyperVisorPresent').toLowerCase() === 'true'); + const hyperv = data[2] ? data[2].toString().toLowerCase() : ''; + result.virtualization = hyperv.indexOf('true') !== -1; + + resolve(result); + }); + } catch (e) { + resolve(result); + } + } + }); + }); + }); +} + +// -------------------------- +// CPU - Processor Data + +function cpu(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + getCpu().then(result => { + if (callback) { callback(result); } + resolve(result); + }); + }); + }); +} + +exports.cpu = cpu; + +// -------------------------- +// CPU - current speed - in GHz + +function getCpuCurrentSpeedSync() { + + let cpus = os.cpus(); + let minFreq = 999999999; + let maxFreq = 0; + let avgFreq = 0; + let cores = []; + + if (cpus && cpus.length) { + for (let i in cpus) { + if ({}.hasOwnProperty.call(cpus, i)) { + let freq = cpus[i].speed > 100 ? (cpus[i].speed + 1) / 1000 : cpus[i].speed / 10; + avgFreq = avgFreq + freq; + if (freq > maxFreq) { maxFreq = freq; } + if (freq < minFreq) { minFreq = freq; } + cores.push(parseFloat(freq.toFixed(2))); + } + } + avgFreq = avgFreq / cpus.length; + return { + min: parseFloat(minFreq.toFixed(2)), + max: parseFloat(maxFreq.toFixed(2)), + avg: parseFloat((avgFreq).toFixed(2)), + cores: cores + }; + } else { + return { + min: 0, + max: 0, + avg: 0, + cores: cores + }; + } +} + +function cpuCurrentSpeed(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + let result = getCpuCurrentSpeedSync(); + if (result.avg === 0 && _cpu_speed !== 0) { + const currCpuSpeed = parseFloat(_cpu_speed); + result = { + min: currCpuSpeed, + max: currCpuSpeed, + avg: currCpuSpeed, + cores: [] + }; + } + if (callback) { callback(result); } + resolve(result); + }); + }); +} + +exports.cpuCurrentSpeed = cpuCurrentSpeed; + +// -------------------------- +// CPU - temperature +// if sensors are installed + +function cpuTemperature(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + let result = { + main: null, + cores: [], + max: null, + socket: [], + chipset: null + }; + if (_linux) { + // CPU Chipset, Socket + try { + const cmd = 'cat /sys/class/thermal/thermal_zone*/type 2>/dev/null; echo "-----"; cat /sys/class/thermal/thermal_zone*/temp 2>/dev/null;'; + const parts = execSync(cmd).toString().split('-----\n'); + if (parts.length === 2) { + const lines = parts[0].split('\n'); + const lines2 = parts[1].split('\n'); + for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim(); + if (line.startsWith('acpi') && lines2[i]) { + result.socket.push(Math.round(parseInt(lines2[i], 10) / 100) / 10); + } + if (line.startsWith('pch') && lines2[i]) { + result.chipset = Math.round(parseInt(lines2[i], 10) / 100) / 10; + } + } + } + } catch (e) { + util.noop(); + } + + const cmd = 'for mon in /sys/class/hwmon/hwmon*; do for label in "$mon"/temp*_label; do if [ -f $label ]; then value=$(echo $label | rev | cut -c 7- | rev)_input; if [ -f "$value" ]; then echo $(cat "$label")___$(cat "$value"); fi; fi; done; done;'; + try { + exec(cmd, function (error, stdout) { + stdout = stdout.toString(); + const tdiePos = stdout.toLowerCase().indexOf('tdie'); + if (tdiePos !== -1) { + stdout = stdout.substring(tdiePos); + } + let lines = stdout.split('\n'); + lines.forEach(line => { + const parts = line.split('___'); + const label = parts[0]; + const value = parts.length > 1 && parts[1] ? parts[1] : '0'; + if (value && (label === undefined || (label && label.toLowerCase().startsWith('core')))) { + result.cores.push(Math.round(parseInt(value, 10) / 100) / 10); + } else if (value && label && result.main === null) { + result.main = Math.round(parseInt(value, 10) / 100) / 10; + } + }); + + if (result.cores.length > 0) { + if (result.main === null) { + result.main = Math.round(result.cores.reduce((a, b) => a + b, 0) / result.cores.length); + } + let maxtmp = Math.max.apply(Math, result.cores); + result.max = (maxtmp > result.main) ? maxtmp : result.main; + } + if (result.main !== null) { + if (result.max === null) { + result.max = result.main; + } + if (callback) { callback(result); } + resolve(result); + return; + } + exec('sensors', function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + let tdieTemp = null; + let newSectionStarts = true; + let section = ''; + lines.forEach(function (line) { + // determine section + if (line.trim() === '') { + newSectionStarts = true; + } else if (newSectionStarts) { + if (line.trim().toLowerCase().startsWith('acpi')) { section = 'acpi'; } + if (line.trim().toLowerCase().startsWith('pch')) { section = 'pch'; } + if (line.trim().toLowerCase().startsWith('core')) { section = 'core'; } + newSectionStarts = false; + } + let regex = /[+-]([^°]*)/g; + let temps = line.match(regex); + let firstPart = line.split(':')[0].toUpperCase(); + if (section === 'acpi') { + // socket temp + if (firstPart.indexOf('TEMP') !== -1) { + result.socket.push(parseFloat(temps)); + } + } else if (section === 'pch') { + // chipset temp + if (firstPart.indexOf('TEMP') !== -1) { + result.chipset = parseFloat(temps); + } + } + // cpu temp + if (firstPart.indexOf('PHYSICAL') !== -1 || firstPart.indexOf('PACKAGE') !== -1) { + result.main = parseFloat(temps); + } + if (firstPart.indexOf('CORE ') !== -1) { + result.cores.push(parseFloat(temps)); + } + if (firstPart.indexOf('TDIE') !== -1 && tdieTemp === null) { + tdieTemp = parseFloat(temps); + } + }); + if (result.cores.length > 0) { + if (result.main === null) { + result.main = Math.round(result.cores.reduce((a, b) => a + b, 0) / result.cores.length); + } + let maxtmp = Math.max.apply(Math, result.cores); + result.max = (maxtmp > result.main) ? maxtmp : result.main; + } else { + if (result.main === null && tdieTemp !== null) { + result.main = tdieTemp; + result.max = tdieTemp; + } + } + if (result.main !== null || result.max !== null) { + if (callback) { callback(result); } + resolve(result); + return; + } + } + fs.stat('/sys/class/thermal/thermal_zone0/temp', function (err) { + if (err === null) { + fs.readFile('/sys/class/thermal/thermal_zone0/temp', function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + if (lines.length > 0) { + result.main = parseFloat(lines[0]) / 1000.0; + result.max = result.main; + } + } + if (callback) { callback(result); } + resolve(result); + }); + } else { + exec('/opt/vc/bin/vcgencmd measure_temp', function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + if (lines.length > 0 && lines[0].indexOf('=')) { + result.main = parseFloat(lines[0].split('=')[1]); + result.max = result.main; + } + } + if (callback) { callback(result); } + resolve(result); + }); + } + }); + }); + }); + } catch (er) { + if (callback) { callback(result); } + resolve(result); + } + } + if (_freebsd || _openbsd || _netbsd) { + exec('sysctl dev.cpu | grep temp', function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + let sum = 0; + lines.forEach(function (line) { + const parts = line.split(':'); + if (parts.length > 1) { + const temp = parseFloat(parts[1].replace(',', '.')); + if (temp > result.max) { result.max = temp; } + sum = sum + temp; + result.cores.push(temp); + } + }); + if (result.cores.length) { + result.main = Math.round(sum / result.cores.length * 100) / 100; + } + } + if (callback) { callback(result); } + resolve(result); + }); + } + if (_darwin) { + let osxTemp = null; + try { + osxTemp = require('osx-temperature-sensor'); + } catch (er) { + osxTemp = null; + } + if (osxTemp) { + result = osxTemp.cpuTemperature(); + } + + if (callback) { callback(result); } + resolve(result); + } + if (_sunos) { + if (callback) { callback(result); } + resolve(result); + } + if (_windows) { + try { + util.powerShell('Get-WmiObject MSAcpi_ThermalZoneTemperature -Namespace "root/wmi" | Select CurrentTemperature').then((stdout, error) => { + if (!error) { + let sum = 0; + let lines = stdout.split('\r\n').filter(line => line.trim() !== '').filter((line, idx) => idx > 0); + lines.forEach(function (line) { + let value = (parseInt(line, 10) - 2732) / 10; + if (!isNaN(value)) { + sum = sum + value; + if (value > result.max) { result.max = value; } + result.cores.push(value); + } + }); + if (result.cores.length) { + result.main = sum / result.cores.length; + } + } + if (callback) { callback(result); } + resolve(result); + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + }); + }); +} + +exports.cpuTemperature = cpuTemperature; + +// -------------------------- +// CPU Flags + +function cpuFlags(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + let result = ''; + if (_windows) { + try { + exec('reg query "HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0" /v FeatureSet', util.execOptsWin, function (error, stdout) { + if (!error) { + let flag_hex = stdout.split('0x').pop().trim(); + let flag_bin_unpadded = parseInt(flag_hex, 16).toString(2); + let flag_bin = '0'.repeat(32 - flag_bin_unpadded.length) + flag_bin_unpadded; + // empty flags are the reserved fields in the CPUID feature bit list + // as found on wikipedia: + // https://en.wikipedia.org/wiki/CPUID + let all_flags = [ + 'fpu', 'vme', 'de', 'pse', 'tsc', 'msr', 'pae', 'mce', 'cx8', 'apic', + '', 'sep', 'mtrr', 'pge', 'mca', 'cmov', 'pat', 'pse-36', 'psn', 'clfsh', + '', 'ds', 'acpi', 'mmx', 'fxsr', 'sse', 'sse2', 'ss', 'htt', 'tm', 'ia64', 'pbe' + ]; + for (let f = 0; f < all_flags.length; f++) { + if (flag_bin[f] === '1' && all_flags[f] !== '') { + result += ' ' + all_flags[f]; + } + } + result = result.trim().toLowerCase(); + } + if (callback) { callback(result); } + resolve(result); + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + if (_linux) { + try { + + exec('export LC_ALL=C; lscpu; unset LC_ALL', function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + lines.forEach(function (line) { + if (line.split(':')[0].toUpperCase().indexOf('FLAGS') !== -1) { + result = line.split(':')[1].trim().toLowerCase(); + } + }); + } + if (!result) { + fs.readFile('/proc/cpuinfo', function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + result = util.getValue(lines, 'features', ':', true).toLowerCase(); + } + if (callback) { callback(result); } + resolve(result); + }); + } else { + if (callback) { callback(result); } + resolve(result); + } + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + if (_freebsd || _openbsd || _netbsd) { + exec('export LC_ALL=C; dmidecode -t 4 2>/dev/null; unset LC_ALL', function (error, stdout) { + let flags = []; + if (!error) { + let parts = stdout.toString().split('\tFlags:'); + const lines = parts.length > 1 ? parts[1].split('\tVersion:')[0].split('\n') : []; + lines.forEach(function (line) { + let flag = (line.indexOf('(') ? line.split('(')[0].toLowerCase() : '').trim().replace(/\t/g, ''); + if (flag) { + flags.push(flag); + } + }); + } + result = flags.join(' ').trim().toLowerCase(); + if (callback) { callback(result); } + resolve(result); + }); + } + if (_darwin) { + exec('sysctl machdep.cpu.features', function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + if (lines.length > 0 && lines[0].indexOf('machdep.cpu.features:') !== -1) { + result = lines[0].split(':')[1].trim().toLowerCase(); + } + } + if (callback) { callback(result); } + resolve(result); + }); + } + if (_sunos) { + if (callback) { callback(result); } + resolve(result); + } + }); + }); +} + +exports.cpuFlags = cpuFlags; + +// -------------------------- +// CPU Cache + +function cpuCache(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + + let result = { + l1d: null, + l1i: null, + l2: null, + l3: null, + }; + if (_linux) { + try { + exec('export LC_ALL=C; lscpu; unset LC_ALL', function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + lines.forEach(function (line) { + let parts = line.split(':'); + if (parts[0].toUpperCase().indexOf('L1D CACHE') !== -1) { + result.l1d = parseInt(parts[1].trim()) * (parts[1].indexOf('M') !== -1 ? 1024 * 1024 : (parts[1].indexOf('K') !== -1 ? 1024 : 1)); + } + if (parts[0].toUpperCase().indexOf('L1I CACHE') !== -1) { + result.l1i = parseInt(parts[1].trim()) * (parts[1].indexOf('M') !== -1 ? 1024 * 1024 : (parts[1].indexOf('K') !== -1 ? 1024 : 1)); + } + if (parts[0].toUpperCase().indexOf('L2 CACHE') !== -1) { + result.l2 = parseInt(parts[1].trim()) * (parts[1].indexOf('M') !== -1 ? 1024 * 1024 : (parts[1].indexOf('K') !== -1 ? 1024 : 1)); + } + if (parts[0].toUpperCase().indexOf('L3 CACHE') !== -1) { + result.l3 = parseInt(parts[1].trim()) * (parts[1].indexOf('M') !== -1 ? 1024 * 1024 : (parts[1].indexOf('K') !== -1 ? 1024 : 1)); + } + }); + } + if (callback) { callback(result); } + resolve(result); + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + if (_freebsd || _openbsd || _netbsd) { + exec('export LC_ALL=C; dmidecode -t 7 2>/dev/null; unset LC_ALL', function (error, stdout) { + let cache = []; + if (!error) { + const data = stdout.toString(); + cache = data.split('Cache Information'); + cache.shift(); + } + for (let i = 0; i < cache.length; i++) { + const lines = cache[i].split('\n'); + let cacheType = util.getValue(lines, 'Socket Designation').toLowerCase().replace(' ', '-').split('-'); + cacheType = cacheType.length ? cacheType[0] : ''; + const sizeParts = util.getValue(lines, 'Installed Size').split(' '); + let size = parseInt(sizeParts[0], 10); + const unit = sizeParts.length > 1 ? sizeParts[1] : 'kb'; + size = size * (unit === 'kb' ? 1024 : (unit === 'mb' ? 1024 * 1024 : (unit === 'gb' ? 1024 * 1024 * 1024 : 1))); + if (cacheType) { + if (cacheType === 'l1') { + result.cache[cacheType + 'd'] = size / 2; + result.cache[cacheType + 'i'] = size / 2; + } else { + result.cache[cacheType] = size; + } + } + } + if (callback) { callback(result); } + resolve(result); + }); + } + if (_darwin) { + exec('sysctl hw.l1icachesize hw.l1dcachesize hw.l2cachesize hw.l3cachesize', function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + lines.forEach(function (line) { + let parts = line.split(':'); + if (parts[0].toLowerCase().indexOf('hw.l1icachesize') !== -1) { + result.l1d = parseInt(parts[1].trim()) * (parts[1].indexOf('K') !== -1 ? 1024 : 1); + } + if (parts[0].toLowerCase().indexOf('hw.l1dcachesize') !== -1) { + result.l1i = parseInt(parts[1].trim()) * (parts[1].indexOf('K') !== -1 ? 1024 : 1); + } + if (parts[0].toLowerCase().indexOf('hw.l2cachesize') !== -1) { + result.l2 = parseInt(parts[1].trim()) * (parts[1].indexOf('K') !== -1 ? 1024 : 1); + } + if (parts[0].toLowerCase().indexOf('hw.l3cachesize') !== -1) { + result.l3 = parseInt(parts[1].trim()) * (parts[1].indexOf('K') !== -1 ? 1024 : 1); + } + }); + } + if (callback) { callback(result); } + resolve(result); + }); + } + if (_sunos) { + if (callback) { callback(result); } + resolve(result); + } + if (_windows) { + try { + util.powerShell('Get-WmiObject Win32_processor | select L2CacheSize, L3CacheSize | fl').then((stdout, error) => { + if (!error) { + let lines = stdout.split('\r\n'); + result.l1d = 0; + result.l1i = 0; + result.l2 = util.getValue(lines, 'l2cachesize', ':'); + result.l3 = util.getValue(lines, 'l3cachesize', ':'); + if (result.l2) { result.l2 = parseInt(result.l2, 10) * 1024; } + if (result.l3) { result.l3 = parseInt(result.l3, 10) * 1024; } + } + util.powerShell('Get-WmiObject Win32_CacheMemory | select CacheType,InstalledSize,Level | fl').then((stdout, error) => { + if (!error) { + const parts = stdout.split(/\n\s*\n/); + parts.forEach(function (part) { + const lines = part.split('\r\n'); + const cacheType = util.getValue(lines, 'CacheType'); + const level = util.getValue(lines, 'Level'); + const installedSize = util.getValue(lines, 'InstalledSize'); + // L1 Instructions + if (level === '3' && cacheType === '3') { + result.l1i = parseInt(installedSize, 10); + } + // L1 Data + if (level === '3' && cacheType === '4') { + result.l1d = parseInt(installedSize, 10); + } + // L1 all + if (level === '3' && cacheType === '5' && !result.l1i && !result.l1d) { + result.l1i = parseInt(installedSize, 10) / 2; + result.l1d = parseInt(installedSize, 10) / 2; + } + }); + } + if (callback) { callback(result); } + resolve(result); + }); + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + }); + }); +} + +exports.cpuCache = cpuCache; + +// -------------------------- +// CPU - current load - in % + +function getLoad() { + + return new Promise((resolve) => { + process.nextTick(() => { + let loads = os.loadavg().map(function (x) { return x / util.cores(); }); + let avgLoad = parseFloat((Math.max.apply(Math, loads)).toFixed(2)); + let result = {}; + + let now = Date.now() - _current_cpu.ms; + if (now >= 200) { + _current_cpu.ms = Date.now(); + const cpus = os.cpus(); + let totalUser = 0; + let totalSystem = 0; + let totalNice = 0; + let totalIrq = 0; + let totalIdle = 0; + let cores = []; + _corecount = (cpus && cpus.length) ? cpus.length : 0; + + for (let i = 0; i < _corecount; i++) { + const cpu = cpus[i].times; + totalUser += cpu.user; + totalSystem += cpu.sys; + totalNice += cpu.nice; + totalIdle += cpu.idle; + totalIrq += cpu.irq; + let tmpTick = (_cpus && _cpus[i] && _cpus[i].totalTick ? _cpus[i].totalTick : 0); + let tmpLoad = (_cpus && _cpus[i] && _cpus[i].totalLoad ? _cpus[i].totalLoad : 0); + let tmpUser = (_cpus && _cpus[i] && _cpus[i].user ? _cpus[i].user : 0); + let tmpSystem = (_cpus && _cpus[i] && _cpus[i].sys ? _cpus[i].sys : 0); + let tmpNice = (_cpus && _cpus[i] && _cpus[i].nice ? _cpus[i].nice : 0); + let tmpIdle = (_cpus && _cpus[i] && _cpus[i].idle ? _cpus[i].idle : 0); + let tmpIrq = (_cpus && _cpus[i] && _cpus[i].irq ? _cpus[i].irq : 0); + _cpus[i] = cpu; + _cpus[i].totalTick = _cpus[i].user + _cpus[i].sys + _cpus[i].nice + _cpus[i].irq + _cpus[i].idle; + _cpus[i].totalLoad = _cpus[i].user + _cpus[i].sys + _cpus[i].nice + _cpus[i].irq; + _cpus[i].currentTick = _cpus[i].totalTick - tmpTick; + _cpus[i].load = (_cpus[i].totalLoad - tmpLoad); + _cpus[i].loadUser = (_cpus[i].user - tmpUser); + _cpus[i].loadSystem = (_cpus[i].sys - tmpSystem); + _cpus[i].loadNice = (_cpus[i].nice - tmpNice); + _cpus[i].loadIdle = (_cpus[i].idle - tmpIdle); + _cpus[i].loadIrq = (_cpus[i].irq - tmpIrq); + cores[i] = {}; + cores[i].load = _cpus[i].load / _cpus[i].currentTick * 100; + cores[i].loadUser = _cpus[i].loadUser / _cpus[i].currentTick * 100; + cores[i].loadSystem = _cpus[i].loadSystem / _cpus[i].currentTick * 100; + cores[i].loadNice = _cpus[i].loadNice / _cpus[i].currentTick * 100; + cores[i].loadIdle = _cpus[i].loadIdle / _cpus[i].currentTick * 100; + cores[i].loadIrq = _cpus[i].loadIrq / _cpus[i].currentTick * 100; + cores[i].rawLoad = _cpus[i].load; + cores[i].rawLoadUser = _cpus[i].loadUser; + cores[i].rawLoadSystem = _cpus[i].loadSystem; + cores[i].rawLoadNice = _cpus[i].loadNice; + cores[i].rawLoadIdle = _cpus[i].loadIdle; + cores[i].rawLoadIrq = _cpus[i].loadIrq; + } + let totalTick = totalUser + totalSystem + totalNice + totalIrq + totalIdle; + let totalLoad = totalUser + totalSystem + totalNice + totalIrq; + let currentTick = totalTick - _current_cpu.tick; + result = { + avgLoad: avgLoad, + currentLoad: (totalLoad - _current_cpu.load) / currentTick * 100, + currentLoadUser: (totalUser - _current_cpu.user) / currentTick * 100, + currentLoadSystem: (totalSystem - _current_cpu.system) / currentTick * 100, + currentLoadNice: (totalNice - _current_cpu.nice) / currentTick * 100, + currentLoadIdle: (totalIdle - _current_cpu.idle) / currentTick * 100, + currentLoadIrq: (totalIrq - _current_cpu.irq) / currentTick * 100, + rawCurrentLoad: (totalLoad - _current_cpu.load), + rawCurrentLoadUser: (totalUser - _current_cpu.user), + rawCurrentLoadSystem: (totalSystem - _current_cpu.system), + rawCurrentLoadNice: (totalNice - _current_cpu.nice), + rawCurrentLoadIdle: (totalIdle - _current_cpu.idle), + rawCurrentLoadIrq: (totalIrq - _current_cpu.irq), + cpus: cores + }; + _current_cpu = { + user: totalUser, + nice: totalNice, + system: totalSystem, + idle: totalIdle, + irq: totalIrq, + tick: totalTick, + load: totalLoad, + ms: _current_cpu.ms, + currentLoad: result.currentLoad, + currentLoadUser: result.currentLoadUser, + currentLoadSystem: result.currentLoadSystem, + currentLoadNice: result.currentLoadNice, + currentLoadIdle: result.currentLoadIdle, + currentLoadIrq: result.currentLoadIrq, + rawCurrentLoad: result.rawCurrentLoad, + rawCurrentLoadUser: result.rawCurrentLoadUser, + rawCurrentLoadSystem: result.rawCurrentLoadSystem, + rawCurrentLoadNice: result.rawCurrentLoadNice, + rawCurrentLoadIdle: result.rawCurrentLoadIdle, + rawCurrentLoadIrq: result.rawCurrentLoadIrq, + }; + } else { + let cores = []; + for (let i = 0; i < _corecount; i++) { + cores[i] = {}; + cores[i].load = _cpus[i].load / _cpus[i].currentTick * 100; + cores[i].loadUser = _cpus[i].loadUser / _cpus[i].currentTick * 100; + cores[i].loadSystem = _cpus[i].loadSystem / _cpus[i].currentTick * 100; + cores[i].loadNice = _cpus[i].loadNice / _cpus[i].currentTick * 100; + cores[i].loadIdle = _cpus[i].loadIdle / _cpus[i].currentTick * 100; + cores[i].loadIrq = _cpus[i].loadIrq / _cpus[i].currentTick * 100; + cores[i].rawLoad = _cpus[i].load; + cores[i].rawLoadUser = _cpus[i].loadUser; + cores[i].rawLoadSystem = _cpus[i].loadSystem; + cores[i].rawLoadNice = _cpus[i].loadNice; + cores[i].rawLoadIdle = _cpus[i].loadIdle; + cores[i].rawLoadIrq = _cpus[i].loadIrq; + } + result = { + avgLoad: avgLoad, + currentLoad: _current_cpu.currentLoad, + currentLoadUser: _current_cpu.currentLoadUser, + currentLoadSystem: _current_cpu.currentLoadSystem, + currentLoadNice: _current_cpu.currentLoadNice, + currentLoadIdle: _current_cpu.currentLoadIdle, + currentLoadIrq: _current_cpu.currentLoadIrq, + rawCurrentLoad: _current_cpu.rawCurrentLoad, + rawCurrentLoadUser: _current_cpu.rawCurrentLoadUser, + rawCurrentLoadSystem: _current_cpu.rawCurrentLoadSystem, + rawCurrentLoadNice: _current_cpu.rawCurrentLoadNice, + rawCurrentLoadIdle: _current_cpu.rawCurrentLoadIdle, + rawCurrentLoadIrq: _current_cpu.rawCurrentLoadIrq, + cpus: cores + }; + } + resolve(result); + }); + }); +} + +function currentLoad(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + getLoad().then(result => { + if (callback) { callback(result); } + resolve(result); + }); + }); + }); +} + +exports.currentLoad = currentLoad; + +// -------------------------- +// PS - full load +// since bootup + +function getFullLoad() { + + return new Promise((resolve) => { + process.nextTick(() => { + + const cpus = os.cpus(); + let totalUser = 0; + let totalSystem = 0; + let totalNice = 0; + let totalIrq = 0; + let totalIdle = 0; + + let result = 0; + + if (cpus && cpus.length) { + for (let i = 0, len = cpus.length; i < len; i++) { + const cpu = cpus[i].times; + totalUser += cpu.user; + totalSystem += cpu.sys; + totalNice += cpu.nice; + totalIrq += cpu.irq; + totalIdle += cpu.idle; + } + let totalTicks = totalIdle + totalIrq + totalNice + totalSystem + totalUser; + result = (totalTicks - totalIdle) / totalTicks * 100.0; + + } else { + result = 0; + } + resolve(result); + }); + }); +} + +function fullLoad(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + getFullLoad().then(result => { + if (callback) { callback(result); } + resolve(result); + }); + }); + }); +} + +exports.fullLoad = fullLoad; diff --git a/node_modules/systeminformation/lib/docker.js b/node_modules/systeminformation/lib/docker.js new file mode 100644 index 0000000..fe93d1a --- /dev/null +++ b/node_modules/systeminformation/lib/docker.js @@ -0,0 +1,764 @@ +'use strict'; +// @ts-check +// ================================================================================== +// docker.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 13. Docker +// ---------------------------------------------------------------------------------- + +const util = require('./util'); +const DockerSocket = require('./dockerSocket'); + +let _platform = process.platform; +const _windows = (_platform === 'win32'); + +let _docker_container_stats = {}; +let _docker_socket; +let _docker_last_read = 0; + + +// -------------------------- +// get containers (parameter all: get also inactive/exited containers) + +function dockerInfo(callback) { + return new Promise((resolve) => { + process.nextTick(() => { + if (!_docker_socket) { + _docker_socket = new DockerSocket(); + } + const result = {}; + + _docker_socket.getInfo(data => { + result.id = data.ID; + result.containers = data.Containers; + result.containersRunning = data.ContainersRunning; + result.containersPaused = data.ContainersPaused; + result.containersStopped = data.ContainersStopped; + result.images = data.Images; + result.driver = data.Driver; + result.memoryLimit = data.MemoryLimit; + result.swapLimit = data.SwapLimit; + result.kernelMemory = data.KernelMemory; + result.cpuCfsPeriod = data.CpuCfsPeriod; + result.cpuCfsQuota = data.CpuCfsQuota; + result.cpuShares = data.CPUShares; + result.cpuSet = data.CPUSet; + result.ipv4Forwarding = data.IPv4Forwarding; + result.bridgeNfIptables = data.BridgeNfIptables; + result.bridgeNfIp6tables = data.BridgeNfIp6tables; + result.debug = data.Debug; + result.nfd = data.NFd; + result.oomKillDisable = data.OomKillDisable; + result.ngoroutines = data.NGoroutines; + result.systemTime = data.SystemTime; + result.loggingDriver = data.LoggingDriver; + result.cgroupDriver = data.CgroupDriver; + result.nEventsListener = data.NEventsListener; + result.kernelVersion = data.KernelVersion; + result.operatingSystem = data.OperatingSystem; + result.osType = data.OSType; + result.architecture = data.Architecture; + result.ncpu = data.NCPU; + result.memTotal = data.MemTotal; + result.dockerRootDir = data.DockerRootDir; + result.httpProxy = data.HttpProxy; + result.httpsProxy = data.HttpsProxy; + result.noProxy = data.NoProxy; + result.name = data.Name; + result.labels = data.Labels; + result.experimentalBuild = data.ExperimentalBuild; + result.serverVersion = data.ServerVersion; + result.clusterStore = data.ClusterStore; + result.clusterAdvertise = data.ClusterAdvertise; + result.defaultRuntime = data.DefaultRuntime; + result.liveRestoreEnabled = data.LiveRestoreEnabled; + result.isolation = data.Isolation; + result.initBinary = data.InitBinary; + result.productLicense = data.ProductLicense; + if (callback) { callback(result); } + resolve(result); + }); + }); + }); +} + +exports.dockerInfo = dockerInfo; + +function dockerImages(all, callback) { + + // fallback - if only callback is given + if (util.isFunction(all) && !callback) { + callback = all; + all = false; + } + if (typeof all === 'string' && all === 'true') { + all = true; + } + if (typeof all !== 'boolean' && all !== undefined) { + all = false; + } + + all = all || false; + let result = []; + return new Promise((resolve) => { + process.nextTick(() => { + if (!_docker_socket) { + _docker_socket = new DockerSocket(); + } + const workload = []; + + _docker_socket.listImages(all, data => { + let dockerImages = {}; + try { + dockerImages = data; + if (dockerImages && Object.prototype.toString.call(dockerImages) === '[object Array]' && dockerImages.length > 0) { + + dockerImages.forEach(function (element) { + + if (element.Names && Object.prototype.toString.call(element.Names) === '[object Array]' && element.Names.length > 0) { + element.Name = element.Names[0].replace(/^\/|\/$/g, ''); + } + workload.push(dockerImagesInspect(element.Id.trim(), element)); + }); + if (workload.length) { + Promise.all( + workload + ).then(data => { + if (callback) { callback(data); } + resolve(data); + }); + } else { + if (callback) { callback(result); } + resolve(result); + } + } else { + if (callback) { callback(result); } + resolve(result); + } + } catch (err) { + if (callback) { callback(result); } + resolve(result); + } + }); + }); + }); +} + +// -------------------------- +// container inspect (for one container) + +function dockerImagesInspect(imageID, payload) { + return new Promise((resolve) => { + process.nextTick(() => { + imageID = imageID || ''; + if (typeof imageID !== 'string') { + return resolve(); + } + const imageIDSanitized = (util.isPrototypePolluted() ? '' : util.sanitizeShellString(imageID, true)).trim(); + if (imageIDSanitized) { + + if (!_docker_socket) { + _docker_socket = new DockerSocket(); + } + + _docker_socket.inspectImage(imageIDSanitized.trim(), data => { + try { + resolve({ + id: payload.Id, + container: data.Container, + comment: data.Comment, + os: data.Os, + architecture: data.Architecture, + parent: data.Parent, + dockerVersion: data.DockerVersion, + size: data.Size, + sharedSize: payload.SharedSize, + virtualSize: data.VirtualSize, + author: data.Author, + created: data.Created ? Math.round(new Date(data.Created).getTime() / 1000) : 0, + containerConfig: data.ContainerConfig ? data.ContainerConfig : {}, + graphDriver: data.GraphDriver ? data.GraphDriver : {}, + repoDigests: data.RepoDigests ? data.RepoDigests : {}, + repoTags: data.RepoTags ? data.RepoTags : {}, + config: data.Config ? data.Config : {}, + rootFS: data.RootFS ? data.RootFS : {}, + }); + } catch (err) { + resolve(); + } + }); + } else { + resolve(); + } + }); + }); +} + +exports.dockerImages = dockerImages; + +function dockerContainers(all, callback) { + + function inContainers(containers, id) { + let filtered = containers.filter(obj => { + /** + * @namespace + * @property {string} Id + */ + return (obj.Id && (obj.Id === id)); + }); + return (filtered.length > 0); + } + + // fallback - if only callback is given + if (util.isFunction(all) && !callback) { + callback = all; + all = false; + } + if (typeof all === 'string' && all === 'true') { + all = true; + } + if (typeof all !== 'boolean' && all !== undefined) { + all = false; + } + + all = all || false; + let result = []; + return new Promise((resolve) => { + process.nextTick(() => { + if (!_docker_socket) { + _docker_socket = new DockerSocket(); + } + const workload = []; + + _docker_socket.listContainers(all, data => { + let docker_containers = {}; + try { + docker_containers = data; + if (docker_containers && Object.prototype.toString.call(docker_containers) === '[object Array]' && docker_containers.length > 0) { + // GC in _docker_container_stats + for (let key in _docker_container_stats) { + if ({}.hasOwnProperty.call(_docker_container_stats, key)) { + if (!inContainers(docker_containers, key)) { delete _docker_container_stats[key]; } + } + } + + docker_containers.forEach(function (element) { + + if (element.Names && Object.prototype.toString.call(element.Names) === '[object Array]' && element.Names.length > 0) { + element.Name = element.Names[0].replace(/^\/|\/$/g, ''); + } + workload.push(dockerContainerInspect(element.Id.trim(), element)); + // result.push({ + // id: element.Id, + // name: element.Name, + // image: element.Image, + // imageID: element.ImageID, + // command: element.Command, + // created: element.Created, + // state: element.State, + // ports: element.Ports, + // mounts: element.Mounts, + // // hostconfig: element.HostConfig, + // // network: element.NetworkSettings + // }); + }); + if (workload.length) { + Promise.all( + workload + ).then(data => { + if (callback) { callback(data); } + resolve(data); + }); + } else { + if (callback) { callback(result); } + resolve(result); + } + } else { + if (callback) { callback(result); } + resolve(result); + } + } catch (err) { + // GC in _docker_container_stats + for (let key in _docker_container_stats) { + if ({}.hasOwnProperty.call(_docker_container_stats, key)) { + if (!inContainers(docker_containers, key)) { delete _docker_container_stats[key]; } + } + } + if (callback) { callback(result); } + resolve(result); + } + }); + }); + }); +} + +// -------------------------- +// container inspect (for one container) + +function dockerContainerInspect(containerID, payload) { + return new Promise((resolve) => { + process.nextTick(() => { + containerID = containerID || ''; + if (typeof containerID !== 'string') { + return resolve(); + } + const containerIdSanitized = (util.isPrototypePolluted() ? '' : util.sanitizeShellString(containerID, true)).trim(); + if (containerIdSanitized) { + + if (!_docker_socket) { + _docker_socket = new DockerSocket(); + } + + _docker_socket.getInspect(containerIdSanitized.trim(), data => { + try { + resolve({ + id: payload.Id, + name: payload.Name, + image: payload.Image, + imageID: payload.ImageID, + command: payload.Command, + created: payload.Created, + started: data.State && data.State.StartedAt ? Math.round(new Date(data.State.StartedAt).getTime() / 1000) : 0, + finished: data.State && data.State.FinishedAt && !data.State.FinishedAt.startsWith('0001-01-01') ? Math.round(new Date(data.State.FinishedAt).getTime() / 1000) : 0, + createdAt: data.Created ? data.Created : '', + startedAt: data.State && data.State.StartedAt ? data.State.StartedAt : '', + finishedAt: data.State && data.State.FinishedAt && !data.State.FinishedAt.startsWith('0001-01-01') ? data.State.FinishedAt : '', + state: payload.State, + restartCount: data.RestartCount || 0, + platform: data.Platform || '', + driver: data.Driver || '', + ports: payload.Ports, + mounts: payload.Mounts, + // hostconfig: payload.HostConfig, + // network: payload.NetworkSettings + }); + } catch (err) { + resolve(); + } + }); + } else { + resolve(); + } + }); + }); +} + +exports.dockerContainers = dockerContainers; + +// -------------------------- +// helper functions for calculation of docker stats + +function docker_calcCPUPercent(cpu_stats, precpu_stats) { + /** + * @namespace + * @property {object} cpu_usage + * @property {number} cpu_usage.total_usage + * @property {number} system_cpu_usage + * @property {object} cpu_usage + * @property {Array} cpu_usage.percpu_usage + */ + + if (!_windows) { + let cpuPercent = 0.0; + // calculate the change for the cpu usage of the container in between readings + let cpuDelta = cpu_stats.cpu_usage.total_usage - precpu_stats.cpu_usage.total_usage; + // calculate the change for the entire system between readings + let systemDelta = cpu_stats.system_cpu_usage - precpu_stats.system_cpu_usage; + + if (systemDelta > 0.0 && cpuDelta > 0.0) { + // calculate the change for the cpu usage of the container in between readings + cpuPercent = (cpuDelta / systemDelta) * cpu_stats.cpu_usage.percpu_usage.length * 100.0; + } + + return cpuPercent; + } else { + let nanoSecNow = util.nanoSeconds(); + let cpuPercent = 0.0; + if (_docker_last_read > 0) { + let possIntervals = (nanoSecNow - _docker_last_read); // / 100 * os.cpus().length; + let intervalsUsed = cpu_stats.cpu_usage.total_usage - precpu_stats.cpu_usage.total_usage; + if (possIntervals > 0) { + cpuPercent = 100.0 * intervalsUsed / possIntervals; + } + } + _docker_last_read = nanoSecNow; + return cpuPercent; + } +} + +function docker_calcNetworkIO(networks) { + let rx; + let wx; + for (let key in networks) { + // skip loop if the property is from prototype + if (!{}.hasOwnProperty.call(networks, key)) { continue; } + + /** + * @namespace + * @property {number} rx_bytes + * @property {number} tx_bytes + */ + let obj = networks[key]; + rx = +obj.rx_bytes; + wx = +obj.tx_bytes; + } + return { + rx, + wx + }; +} + +function docker_calcBlockIO(blkio_stats) { + let result = { + r: 0, + w: 0 + }; + + /** + * @namespace + * @property {Array} io_service_bytes_recursive + */ + if (blkio_stats && blkio_stats.io_service_bytes_recursive && Object.prototype.toString.call(blkio_stats.io_service_bytes_recursive) === '[object Array]' && blkio_stats.io_service_bytes_recursive.length > 0) { + blkio_stats.io_service_bytes_recursive.forEach(function (element) { + /** + * @namespace + * @property {string} op + * @property {number} value + */ + + if (element.op && element.op.toLowerCase() === 'read' && element.value) { + result.r += element.value; + } + if (element.op && element.op.toLowerCase() === 'write' && element.value) { + result.w += element.value; + } + }); + } + return result; +} + +function dockerContainerStats(containerIDs, callback) { + + let containerArray = []; + return new Promise((resolve) => { + process.nextTick(() => { + + // fallback - if only callback is given + if (util.isFunction(containerIDs) && !callback) { + callback = containerIDs; + containerArray = ['*']; + } else { + containerIDs = containerIDs || '*'; + if (typeof containerIDs !== 'string') { + if (callback) { callback([]); } + return resolve([]); + } + let containerIDsSanitized = ''; + containerIDsSanitized.__proto__.toLowerCase = util.stringToLower; + containerIDsSanitized.__proto__.replace = util.stringReplace; + containerIDsSanitized.__proto__.trim = util.stringTrim; + + containerIDsSanitized = containerIDs; + containerIDsSanitized = containerIDsSanitized.trim(); + if (containerIDsSanitized !== '*') { + containerIDsSanitized = ''; + const s = (util.isPrototypePolluted() ? '' : util.sanitizeShellString(containerIDs, true)).trim(); + for (let i = 0; i <= util.mathMin(s.length, 2000); i++) { + if (!(s[i] === undefined)) { + s[i].__proto__.toLowerCase = util.stringToLower; + const sl = s[i].toLowerCase(); + if (sl && sl[0] && !sl[1]) { + containerIDsSanitized = containerIDsSanitized + sl[0]; + } + } + } + } + + containerIDsSanitized = containerIDsSanitized.trim().toLowerCase().replace(/,+/g, '|'); + containerArray = containerIDsSanitized.split('|'); + } + + const result = []; + + const workload = []; + if (containerArray.length && containerArray[0].trim() === '*') { + containerArray = []; + dockerContainers().then(allContainers => { + for (let container of allContainers) { + containerArray.push(container.id); + } + if (containerArray.length) { + dockerContainerStats(containerArray.join(',')).then(result => { + if (callback) { callback(result); } + resolve(result); + }); + } else { + if (callback) { callback(result); } + resolve(result); + } + }); + } else { + for (let containerID of containerArray) { + workload.push(dockerContainerStatsSingle(containerID.trim())); + } + if (workload.length) { + Promise.all( + workload + ).then(data => { + if (callback) { callback(data); } + resolve(data); + }); + } else { + if (callback) { callback(result); } + resolve(result); + } + } + }); + }); +} + +// -------------------------- +// container stats (for one container) + +function dockerContainerStatsSingle(containerID) { + containerID = containerID || ''; + let result = { + id: containerID, + memUsage: 0, + memLimit: 0, + memPercent: 0, + cpuPercent: 0, + pids: 0, + netIO: { + rx: 0, + wx: 0 + }, + blockIO: { + r: 0, + w: 0 + }, + restartCount: 0, + cpuStats: {}, + precpuStats: {}, + memoryStats: {}, + networks: {}, + }; + return new Promise((resolve) => { + process.nextTick(() => { + if (containerID) { + + if (!_docker_socket) { + _docker_socket = new DockerSocket(); + } + + _docker_socket.getInspect(containerID, dataInspect => { + try { + _docker_socket.getStats(containerID, data => { + try { + let stats = data; + + if (!stats.message) { + result.memUsage = (stats.memory_stats && stats.memory_stats.usage ? stats.memory_stats.usage : 0); + result.memLimit = (stats.memory_stats && stats.memory_stats.limit ? stats.memory_stats.limit : 0); + result.memPercent = (stats.memory_stats && stats.memory_stats.usage && stats.memory_stats.limit ? stats.memory_stats.usage / stats.memory_stats.limit * 100.0 : 0); + result.cpuPercent = (stats.cpu_stats && stats.precpu_stats ? docker_calcCPUPercent(stats.cpu_stats, stats.precpu_stats) : 0); + result.pids = (stats.pids_stats && stats.pids_stats.current ? stats.pids_stats.current : 0); + result.restartCount = (dataInspect.RestartCount ? dataInspect.RestartCount : 0); + if (stats.networks) { result.netIO = docker_calcNetworkIO(stats.networks); } + if (stats.blkio_stats) { result.blockIO = docker_calcBlockIO(stats.blkio_stats); } + result.cpuStats = (stats.cpu_stats ? stats.cpu_stats : {}); + result.precpuStats = (stats.precpu_stats ? stats.precpu_stats : {}); + result.memoryStats = (stats.memory_stats ? stats.memory_stats : {}); + result.networks = (stats.networks ? stats.networks : {}); + } + } catch (err) { + util.noop(); + } + // } + resolve(result); + }); + } catch (err) { + util.noop(); + } + }); + } else { + resolve(result); + } + }); + }); +} + +exports.dockerContainerStats = dockerContainerStats; + +// -------------------------- +// container processes (for one container) + +function dockerContainerProcesses(containerID, callback) { + let result = []; + return new Promise((resolve) => { + process.nextTick(() => { + containerID = containerID || ''; + if (typeof containerID !== 'string') { + return resolve(result); + } + const containerIdSanitized = (util.isPrototypePolluted() ? '' : util.sanitizeShellString(containerID, true)).trim(); + + if (containerIdSanitized) { + + if (!_docker_socket) { + _docker_socket = new DockerSocket(); + } + + _docker_socket.getProcesses(containerIdSanitized, data => { + /** + * @namespace + * @property {Array} Titles + * @property {Array} Processes + **/ + try { + if (data && data.Titles && data.Processes) { + let titles = data.Titles.map(function (value) { + return value.toUpperCase(); + }); + let pos_pid = titles.indexOf('PID'); + let pos_ppid = titles.indexOf('PPID'); + let pos_pgid = titles.indexOf('PGID'); + let pos_vsz = titles.indexOf('VSZ'); + let pos_time = titles.indexOf('TIME'); + let pos_elapsed = titles.indexOf('ELAPSED'); + let pos_ni = titles.indexOf('NI'); + let pos_ruser = titles.indexOf('RUSER'); + let pos_user = titles.indexOf('USER'); + let pos_rgroup = titles.indexOf('RGROUP'); + let pos_group = titles.indexOf('GROUP'); + let pos_stat = titles.indexOf('STAT'); + let pos_rss = titles.indexOf('RSS'); + let pos_command = titles.indexOf('COMMAND'); + + data.Processes.forEach(process => { + result.push({ + pidHost: (pos_pid >= 0 ? process[pos_pid] : ''), + ppid: (pos_ppid >= 0 ? process[pos_ppid] : ''), + pgid: (pos_pgid >= 0 ? process[pos_pgid] : ''), + user: (pos_user >= 0 ? process[pos_user] : ''), + ruser: (pos_ruser >= 0 ? process[pos_ruser] : ''), + group: (pos_group >= 0 ? process[pos_group] : ''), + rgroup: (pos_rgroup >= 0 ? process[pos_rgroup] : ''), + stat: (pos_stat >= 0 ? process[pos_stat] : ''), + time: (pos_time >= 0 ? process[pos_time] : ''), + elapsed: (pos_elapsed >= 0 ? process[pos_elapsed] : ''), + nice: (pos_ni >= 0 ? process[pos_ni] : ''), + rss: (pos_rss >= 0 ? process[pos_rss] : ''), + vsz: (pos_vsz >= 0 ? process[pos_vsz] : ''), + command: (pos_command >= 0 ? process[pos_command] : '') + }); + }); + } + } catch (err) { + util.noop(); + } + if (callback) { callback(result); } + resolve(result); + }); + } else { + if (callback) { callback(result); } + resolve(result); + } + }); + }); +} + +exports.dockerContainerProcesses = dockerContainerProcesses; + +function dockerVolumes(callback) { + + let result = []; + return new Promise((resolve) => { + process.nextTick(() => { + if (!_docker_socket) { + _docker_socket = new DockerSocket(); + } + _docker_socket.listVolumes(data => { + let dockerVolumes = {}; + try { + dockerVolumes = data; + if (dockerVolumes && dockerVolumes.Volumes && Object.prototype.toString.call(dockerVolumes.Volumes) === '[object Array]' && dockerVolumes.Volumes.length > 0) { + + dockerVolumes.Volumes.forEach(function (element) { + + result.push({ + name: element.Name, + driver: element.Driver, + labels: element.Labels, + mountpoint: element.Mountpoint, + options: element.Options, + scope: element.Scope, + created: element.CreatedAt ? Math.round(new Date(element.CreatedAt).getTime() / 1000) : 0, + }); + }); + if (callback) { callback(result); } + resolve(result); + } else { + if (callback) { callback(result); } + resolve(result); + } + } catch (err) { + if (callback) { callback(result); } + resolve(result); + } + }); + }); + }); +} + +exports.dockerVolumes = dockerVolumes; +function dockerAll(callback) { + return new Promise((resolve) => { + process.nextTick(() => { + dockerContainers(true).then(result => { + if (result && Object.prototype.toString.call(result) === '[object Array]' && result.length > 0) { + let l = result.length; + result.forEach(function (element) { + dockerContainerStats(element.id).then(res => { + // include stats in array + element.memUsage = res[0].memUsage; + element.memLimit = res[0].memLimit; + element.memPercent = res[0].memPercent; + element.cpuPercent = res[0].cpuPercent; + element.pids = res[0].pids; + element.netIO = res[0].netIO; + element.blockIO = res[0].blockIO; + element.cpuStats = res[0].cpuStats; + element.precpuStats = res[0].precpuStats; + element.memoryStats = res[0].memoryStats; + element.networks = res[0].networks; + + dockerContainerProcesses(element.id).then(processes => { + element.processes = processes; + + l -= 1; + if (l === 0) { + if (callback) { callback(result); } + resolve(result); + } + }); + // all done?? + }); + }); + } else { + if (callback) { callback(result); } + resolve(result); + } + }); + }); + }); +} + +exports.dockerAll = dockerAll; diff --git a/node_modules/systeminformation/lib/dockerSocket.js b/node_modules/systeminformation/lib/dockerSocket.js new file mode 100644 index 0000000..6508dd1 --- /dev/null +++ b/node_modules/systeminformation/lib/dockerSocket.js @@ -0,0 +1,327 @@ +'use strict'; +// @ts-check +// ================================================================================== +// dockerSockets.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 13. DockerSockets +// ---------------------------------------------------------------------------------- + +const net = require('net'); +const isWin = require('os').type() === 'Windows_NT'; +const socketPath = isWin ? '//./pipe/docker_engine' : '/var/run/docker.sock'; + +class DockerSocket { + + getInfo(callback) { + try { + + let socket = net.createConnection({ path: socketPath }); + let alldata = ''; + let data; + + socket.on('connect', () => { + socket.write('GET http:/info HTTP/1.0\r\n\r\n'); + }); + + socket.on('data', data => { + alldata = alldata + data.toString(); + }); + + socket.on('error', () => { + socket = false; + callback({}); + }); + + socket.on('end', () => { + let startbody = alldata.indexOf('\r\n\r\n'); + alldata = alldata.substring(startbody + 4); + socket = false; + try { + data = JSON.parse(alldata); + callback(data); + } catch (err) { + callback({}); + } + }); + } catch (err) { + callback({}); + } + } + + listImages(all, callback) { + try { + + let socket = net.createConnection({ path: socketPath }); + let alldata = ''; + let data; + + socket.on('connect', () => { + socket.write('GET http:/images/json' + (all ? '?all=1' : '') + ' HTTP/1.0\r\n\r\n'); + }); + + socket.on('data', data => { + alldata = alldata + data.toString(); + }); + + socket.on('error', () => { + socket = false; + callback({}); + }); + + socket.on('end', () => { + let startbody = alldata.indexOf('\r\n\r\n'); + alldata = alldata.substring(startbody + 4); + socket = false; + try { + data = JSON.parse(alldata); + callback(data); + } catch (err) { + callback({}); + } + }); + } catch (err) { + callback({}); + } + } + + inspectImage(id, callback) { + id = id || ''; + if (id) { + try { + let socket = net.createConnection({ path: socketPath }); + let alldata = ''; + let data; + + socket.on('connect', () => { + socket.write('GET http:/images/' + id + '/json?stream=0 HTTP/1.0\r\n\r\n'); + }); + + socket.on('data', data => { + alldata = alldata + data.toString(); + }); + + socket.on('error', () => { + socket = false; + callback({}); + }); + + socket.on('end', () => { + let startbody = alldata.indexOf('\r\n\r\n'); + alldata = alldata.substring(startbody + 4); + socket = false; + try { + data = JSON.parse(alldata); + callback(data); + } catch (err) { + callback({}); + } + }); + } catch (err) { + callback({}); + } + } else { + callback({}); + } + } + + listContainers(all, callback) { + try { + + let socket = net.createConnection({ path: socketPath }); + let alldata = ''; + let data; + + socket.on('connect', () => { + socket.write('GET http:/containers/json' + (all ? '?all=1' : '') + ' HTTP/1.0\r\n\r\n'); + }); + + socket.on('data', data => { + alldata = alldata + data.toString(); + }); + + socket.on('error', () => { + socket = false; + callback({}); + }); + + socket.on('end', () => { + let startbody = alldata.indexOf('\r\n\r\n'); + alldata = alldata.substring(startbody + 4); + socket = false; + try { + data = JSON.parse(alldata); + callback(data); + } catch (err) { + callback({}); + } + }); + } catch (err) { + callback({}); + } + } + + getStats(id, callback) { + id = id || ''; + if (id) { + try { + let socket = net.createConnection({ path: socketPath }); + let alldata = ''; + let data; + + socket.on('connect', () => { + socket.write('GET http:/containers/' + id + '/stats?stream=0 HTTP/1.0\r\n\r\n'); + }); + + socket.on('data', data => { + alldata = alldata + data.toString(); + }); + + socket.on('error', () => { + socket = false; + callback({}); + }); + + socket.on('end', () => { + let startbody = alldata.indexOf('\r\n\r\n'); + alldata = alldata.substring(startbody + 4); + socket = false; + try { + data = JSON.parse(alldata); + callback(data); + } catch (err) { + callback({}); + } + }); + } catch (err) { + callback({}); + } + } else { + callback({}); + } + } + + getInspect(id, callback) { + id = id || ''; + if (id) { + try { + let socket = net.createConnection({ path: socketPath }); + let alldata = ''; + let data; + + socket.on('connect', () => { + socket.write('GET http:/containers/' + id + '/json?stream=0 HTTP/1.0\r\n\r\n'); + }); + + socket.on('data', data => { + alldata = alldata + data.toString(); + }); + + socket.on('error', () => { + socket = false; + callback({}); + }); + + socket.on('end', () => { + let startbody = alldata.indexOf('\r\n\r\n'); + alldata = alldata.substring(startbody + 4); + socket = false; + try { + data = JSON.parse(alldata); + callback(data); + } catch (err) { + callback({}); + } + }); + } catch (err) { + callback({}); + } + } else { + callback({}); + } + } + + getProcesses(id, callback) { + id = id || ''; + if (id) { + try { + let socket = net.createConnection({ path: socketPath }); + let alldata = ''; + let data; + + socket.on('connect', () => { + socket.write('GET http:/containers/' + id + '/top?ps_args=-opid,ppid,pgid,vsz,time,etime,nice,ruser,user,rgroup,group,stat,rss,args HTTP/1.0\r\n\r\n'); + }); + + socket.on('data', data => { + alldata = alldata + data.toString(); + }); + + socket.on('error', () => { + socket = false; + callback({}); + }); + + socket.on('end', () => { + let startbody = alldata.indexOf('\r\n\r\n'); + alldata = alldata.substring(startbody + 4); + socket = false; + try { + data = JSON.parse(alldata); + callback(data); + } catch (err) { + callback({}); + } + }); + } catch (err) { + callback({}); + } + } else { + callback({}); + } + } + + listVolumes(callback) { + try { + + let socket = net.createConnection({ path: socketPath }); + let alldata = ''; + let data; + + socket.on('connect', () => { + socket.write('GET http:/volumes HTTP/1.0\r\n\r\n'); + }); + + socket.on('data', data => { + alldata = alldata + data.toString(); + }); + + socket.on('error', () => { + socket = false; + callback({}); + }); + + socket.on('end', () => { + let startbody = alldata.indexOf('\r\n\r\n'); + alldata = alldata.substring(startbody + 4); + socket = false; + try { + data = JSON.parse(alldata); + callback(data); + } catch (err) { + callback({}); + } + }); + } catch (err) { + callback({}); + } + } +} + +module.exports = DockerSocket; diff --git a/node_modules/systeminformation/lib/filesystem.js b/node_modules/systeminformation/lib/filesystem.js new file mode 100644 index 0000000..4dd4730 --- /dev/null +++ b/node_modules/systeminformation/lib/filesystem.js @@ -0,0 +1,1283 @@ +'use strict'; +// @ts-check +// ================================================================================== +// filesystem.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 8. File System +// ---------------------------------------------------------------------------------- + +const util = require('./util'); +const fs = require('fs'); + +const exec = require('child_process').exec; +const execSync = require('child_process').execSync; +const execPromiseSave = util.promisifySave(require('child_process').exec); + +let _platform = process.platform; + +const _linux = (_platform === 'linux' || _platform === 'android'); +const _darwin = (_platform === 'darwin'); +const _windows = (_platform === 'win32'); +const _freebsd = (_platform === 'freebsd'); +const _openbsd = (_platform === 'openbsd'); +const _netbsd = (_platform === 'netbsd'); +const _sunos = (_platform === 'sunos'); + +let _fs_speed = {}; +let _disk_io = {}; + +// -------------------------- +// FS - mounted file systems + +function fsSize(callback) { + + let macOsDisks = []; + + function getmacOsFsType(fs) { + if (!fs.startsWith('/')) { return 'NFS'; } + const parts = fs.split('/'); + const fsShort = parts[parts.length - 1]; + const macOsDisksSingle = macOsDisks.filter(item => item.indexOf(fsShort) >= 0); + if (macOsDisksSingle.length === 1 && macOsDisksSingle[0].indexOf('APFS') >= 0) { return 'APFS'; } + return 'HFS'; + } + + function isLinuxTmpFs(fs) { + const linuxTmpFileSystems = ['rootfs', 'unionfs', 'squashfs', 'cramfs', 'initrd', 'initramfs', 'devtmpfs', 'tmpfs', 'udev', 'devfs', 'specfs', 'type']; + return (linuxTmpFileSystems.includes(fs.toLowerCase())); + } + + function parseDf(lines) { + let data = []; + lines.forEach(function (line) { + if (line !== '') { + line = line.replace(/ +/g, ' ').split(' '); + if (line && ((line[0].startsWith('/')) || (line[6] && line[6] === '/') || (line[0].indexOf('/') > 0) || (line[0].indexOf(':') === 1) || !_darwin && !isLinuxTmpFs(line[1]))) { + const fs = line[0]; + const fsType = ((_linux || _freebsd || _openbsd || _netbsd) ? line[1] : getmacOsFsType(line[0])); + const size = parseInt(((_linux || _freebsd || _openbsd || _netbsd) ? line[2] : line[1])) * 1024; + const used = parseInt(((_linux || _freebsd || _openbsd || _netbsd) ? line[3] : line[2])) * 1024; + const available = parseInt(((_linux || _freebsd || _openbsd || _netbsd) ? line[4] : line[3])) * 1024; + const use = parseFloat((100.0 * (used / (used + available))).toFixed(2)); + line.splice(0, (_linux || _freebsd || _openbsd || _netbsd) ? 6 : 5); + const mount = line.join(' '); + // const mount = line[line.length - 1]; + if (!data.find(el => (el.fs === fs && el.type === fsType))) { + data.push({ + fs, + type: fsType, + size, + used, + available, + use, + mount + }); + } + } + } + }); + return data; + } + + return new Promise((resolve) => { + process.nextTick(() => { + let data = []; + if (_linux || _freebsd || _openbsd || _netbsd || _darwin) { + let cmd = ''; + if (_darwin) { + cmd = 'df -kP'; + try { + macOsDisks = execSync('diskutil list').toString().split('\n').filter(line => { + return !line.startsWith('/') && line.indexOf(':') > 0; + }); + } catch (e) { + macOsDisks = []; + } + } + if (_linux) { cmd = 'df -lkPTx squashfs'; } // cmd = 'df -lkPTx squashfs | grep -E "^/|^.\\:"'; + if (_freebsd || _openbsd || _netbsd) { cmd = 'df -lkPT'; } + exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + if (lines && lines[0] && lines[0].toLowerCase().startsWith('filesystem')) { lines.shift(); } + data = parseDf(lines); + if (callback) { + callback(data); + } + resolve(data); + } else { + exec('df -kPT', { maxBuffer: 1024 * 1024 }, function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + if (lines && lines[0] && lines[0].toLowerCase().startsWith('filesystem')) { lines.shift(); } + data = parseDf(lines); + } + if (callback) { + callback(data); + } + resolve(data); + }); + } + }); + } + if (_sunos) { + if (callback) { callback(data); } + resolve(data); + } + if (_windows) { + try { + // util.wmic('logicaldisk get Caption,FileSystem,FreeSpace,Size').then((stdout) => { + util.powerShell('Get-WmiObject Win32_logicaldisk | select Caption,FileSystem,FreeSpace,Size | fl').then((stdout, error) => { + if (!error) { + let devices = stdout.toString().split(/\n\s*\n/); + devices.forEach(function (device) { + let lines = device.split('\r\n'); + const size = util.toInt(util.getValue(lines, 'size', ':')); + const free = util.toInt(util.getValue(lines, 'freespace', ':')); + const caption = util.getValue(lines, 'caption', ':'); + if (size) { + data.push({ + fs: caption, + type: util.getValue(lines, 'filesystem', ':'), + size, + used: size - free, + available: free, + use: parseFloat(((100.0 * (size - free)) / size).toFixed(2)), + mount: caption + }); + } + }); + } + if (callback) { + callback(data); + } + resolve(data); + }); + } catch (e) { + if (callback) { callback(data); } + resolve(data); + } + } + }); + }); +} + +exports.fsSize = fsSize; + +// -------------------------- +// FS - open files count + +function fsOpenFiles(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + const result = { + max: null, + allocated: null, + available: null + }; + if (_freebsd || _openbsd || _netbsd || _darwin) { + let cmd = 'sysctl -i kern.maxfiles kern.num_files kern.open_files'; + exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + result.max = parseInt(util.getValue(lines, 'kern.maxfiles', ':'), 10); + result.allocated = parseInt(util.getValue(lines, 'kern.num_files', ':'), 10) || parseInt(util.getValue(lines, 'kern.open_files', ':'), 10); + result.available = result.max - result.allocated; + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + if (_linux) { + fs.readFile('/proc/sys/fs/file-nr', function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + if (lines[0]) { + const parts = lines[0].replace(/\s+/g, ' ').split(' '); + if (parts.length === 3) { + result.allocated = parseInt(parts[0], 10); + result.available = parseInt(parts[1], 10); + result.max = parseInt(parts[2], 10); + if (!result.available) { result.available = result.max - result.allocated; } + } + } + if (callback) { + callback(result); + } + resolve(result); + } else { + fs.readFile('/proc/sys/fs/file-max', function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + if (lines[0]) { + result.max = parseInt(lines[0], 10); + } + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + }); + } + if (_sunos) { + if (callback) { callback(null); } + resolve(null); + } + if (_windows) { + if (callback) { callback(null); } + resolve(null); + } + }); + }); +} + +exports.fsOpenFiles = fsOpenFiles; + +// -------------------------- +// disks + +function parseBytes(s) { + return parseInt(s.substr(s.indexOf(' (') + 2, s.indexOf(' Bytes)') - 10)); +} + +function parseDevices(lines) { + let devices = []; + let i = 0; + lines.forEach(line => { + if (line.length > 0) { + if (line[0] === '*') { + i++; + } else { + let parts = line.split(':'); + if (parts.length > 1) { + if (!devices[i]) { + devices[i] = { + name: '', + identifier: '', + type: 'disk', + fsType: '', + mount: '', + size: 0, + physical: 'HDD', + uuid: '', + label: '', + model: '', + serial: '', + removable: false, + protocol: '' + }; + } + parts[0] = parts[0].trim().toUpperCase().replace(/ +/g, ''); + parts[1] = parts[1].trim(); + if ('DEVICEIDENTIFIER' === parts[0]) { devices[i].identifier = parts[1]; } + if ('DEVICENODE' === parts[0]) { devices[i].name = parts[1]; } + if ('VOLUMENAME' === parts[0]) { + if (parts[1].indexOf('Not applicable') === -1) { devices[i].label = parts[1]; } + } + if ('PROTOCOL' === parts[0]) { devices[i].protocol = parts[1]; } + if ('DISKSIZE' === parts[0]) { devices[i].size = parseBytes(parts[1]); } + if ('FILESYSTEMPERSONALITY' === parts[0]) { devices[i].fsType = parts[1]; } + if ('MOUNTPOINT' === parts[0]) { devices[i].mount = parts[1]; } + if ('VOLUMEUUID' === parts[0]) { devices[i].uuid = parts[1]; } + if ('READ-ONLYMEDIA' === parts[0] && parts[1] === 'Yes') { devices[i].physical = 'CD/DVD'; } + if ('SOLIDSTATE' === parts[0] && parts[1] === 'Yes') { devices[i].physical = 'SSD'; } + if ('VIRTUAL' === parts[0]) { devices[i].type = 'virtual'; } + if ('REMOVABLEMEDIA' === parts[0]) { devices[i].removable = (parts[1] === 'Removable'); } + if ('PARTITIONTYPE' === parts[0]) { devices[i].type = 'part'; } + if ('DEVICE/MEDIANAME' === parts[0]) { devices[i].model = parts[1]; } + } + } + } + }); + return devices; +} + +function parseBlk(lines) { + let data = []; + + lines.filter(line => line !== '').forEach((line) => { + try { + line = decodeURIComponent(line.replace(/\\x/g, '%')); + line = line.replace(/\\/g, '\\\\'); + let disk = JSON.parse(line); + data.push({ + 'name': disk.name, + 'type': disk.type, + 'fsType': disk.fsType, + 'mount': disk.mountpoint, + 'size': parseInt(disk.size), + 'physical': (disk.type === 'disk' ? (disk.rota === '0' ? 'SSD' : 'HDD') : (disk.type === 'rom' ? 'CD/DVD' : '')), + 'uuid': disk.uuid, + 'label': disk.label, + 'model': disk.model, + 'serial': disk.serial, + 'removable': disk.rm === '1', + 'protocol': disk.tran, + 'group': disk.group, + }); + } catch (e) { + util.noop(); + } + }); + data = util.unique(data); + data = util.sortByKey(data, ['type', 'name']); + return data; +} + +function blkStdoutToObject(stdout) { + return stdout.toString() + .replace(/NAME=/g, '{"name":') + .replace(/FSTYPE=/g, ',"fsType":') + .replace(/TYPE=/g, ',"type":') + .replace(/SIZE=/g, ',"size":') + .replace(/MOUNTPOINT=/g, ',"mountpoint":') + .replace(/UUID=/g, ',"uuid":') + .replace(/ROTA=/g, ',"rota":') + .replace(/RO=/g, ',"ro":') + .replace(/RM=/g, ',"rm":') + .replace(/TRAN=/g, ',"tran":') + .replace(/SERIAL=/g, ',"serial":') + .replace(/LABEL=/g, ',"label":') + .replace(/MODEL=/g, ',"model":') + .replace(/OWNER=/g, ',"owner":') + .replace(/GROUP=/g, ',"group":') + .replace(/\n/g, '}\n'); +} + +function blockDevices(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + let data = []; + if (_linux) { + // see https://wiki.ubuntuusers.de/lsblk/ + // 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) { + 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) { + if (!error) { + let lines = blkStdoutToObject(stdout).split('\n'); + data = parseBlk(lines); + if (callback) { + callback(data); + } + resolve(data); + } else { + exec('lsblk -bPo NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,UUID,ROTA,RO,RM,LABEL,MODEL,OWNER 2>/dev/null', { maxBuffer: 1024 * 1024 }, function (error, stdout) { + if (!error) { + let lines = blkStdoutToObject(stdout).split('\n'); + data = parseBlk(lines); + } + if (callback) { + callback(data); + } + resolve(data); + }); + } + }); + } + if (_darwin) { + exec('diskutil info -all', { maxBuffer: 1024 * 1024 }, function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + // parse lines into temp array of devices + data = parseDevices(lines); + } + if (callback) { + callback(data); + } + resolve(data); + }); + } + if (_sunos) { + if (callback) { callback(data); } + resolve(data); + } + if (_windows) { + let drivetypes = ['Unknown', 'NoRoot', 'Removable', 'Local', 'Network', 'CD/DVD', 'RAM']; + try { + // util.wmic('logicaldisk get Caption,Description,DeviceID,DriveType,FileSystem,FreeSpace,Name,Size,VolumeName,VolumeSerialNumber /value').then((stdout, error) => { + // util.powerShell('Get-WmiObject Win32_logicaldisk | select Caption,DriveType,Name,FileSystem,Size,VolumeSerialNumber,VolumeName | fl').then((stdout, error) => { + util.powerShell('Get-CimInstance -ClassName Win32_LogicalDisk | select Caption,DriveType,Name,FileSystem,Size,VolumeSerialNumber,VolumeName | fl').then((stdout, error) => { + if (!error) { + let devices = stdout.toString().split(/\n\s*\n/); + devices.forEach(function (device) { + let lines = device.split('\r\n'); + let drivetype = util.getValue(lines, 'drivetype', ':'); + if (drivetype) { + data.push({ + name: util.getValue(lines, 'name', ':'), + identifier: util.getValue(lines, 'caption', ':'), + type: 'disk', + fsType: util.getValue(lines, 'filesystem', ':').toLowerCase(), + mount: util.getValue(lines, 'caption', ':'), + size: util.getValue(lines, 'size', ':'), + physical: (drivetype >= 0 && drivetype <= 6) ? drivetypes[drivetype] : drivetypes[0], + uuid: util.getValue(lines, 'volumeserialnumber', ':'), + label: util.getValue(lines, 'volumename', ':'), + model: '', + serial: util.getValue(lines, 'volumeserialnumber', ':'), + removable: drivetype === '2', + protocol: '' + }); + } + }); + } + if (callback) { + callback(data); + } + resolve(data); + }); + } catch (e) { + if (callback) { callback(data); } + resolve(data); + } + } + if (_freebsd || _openbsd || _netbsd) { + // will follow + if (callback) { callback(null); } + resolve(null); + } + + }); + }); +} + +exports.blockDevices = blockDevices; + +// -------------------------- +// FS - speed + +function calcFsSpeed(rx, wx) { + let result = { + rx: 0, + wx: 0, + tx: 0, + rx_sec: null, + wx_sec: null, + tx_sec: null, + ms: 0 + }; + + if (_fs_speed && _fs_speed.ms) { + result.rx = rx; + result.wx = wx; + result.tx = result.rx + result.wx; + result.ms = Date.now() - _fs_speed.ms; + result.rx_sec = (result.rx - _fs_speed.bytes_read) / (result.ms / 1000); + result.wx_sec = (result.wx - _fs_speed.bytes_write) / (result.ms / 1000); + result.tx_sec = result.rx_sec + result.wx_sec; + _fs_speed.rx_sec = result.rx_sec; + _fs_speed.wx_sec = result.wx_sec; + _fs_speed.tx_sec = result.tx_sec; + _fs_speed.bytes_read = result.rx; + _fs_speed.bytes_write = result.wx; + _fs_speed.bytes_overall = result.rx + result.wx; + _fs_speed.ms = Date.now(); + _fs_speed.last_ms = result.ms; + } else { + result.rx = rx; + result.wx = wx; + result.tx = result.rx + result.wx; + _fs_speed.rx_sec = null; + _fs_speed.wx_sec = null; + _fs_speed.tx_sec = null; + _fs_speed.bytes_read = result.rx; + _fs_speed.bytes_write = result.wx; + _fs_speed.bytes_overall = result.rx + result.wx; + _fs_speed.ms = Date.now(); + _fs_speed.last_ms = 0; + } + return result; +} + +function fsStats(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + if (_windows || _freebsd || _openbsd || _netbsd || _sunos) { + return resolve(null); + } + + let result = { + rx: 0, + wx: 0, + tx: 0, + rx_sec: null, + wx_sec: null, + tx_sec: null, + ms: 0 + }; + + let rx = 0; + let wx = 0; + if ((_fs_speed && !_fs_speed.ms) || (_fs_speed && _fs_speed.ms && Date.now() - _fs_speed.ms >= 500)) { + if (_linux) { + // exec("df -k | grep /dev/", function(error, stdout) { + exec('lsblk -r 2>/dev/null | grep /', { maxBuffer: 1024 * 1024 }, function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + let fs_filter = []; + lines.forEach(function (line) { + if (line !== '') { + line = line.trim().split(' '); + if (fs_filter.indexOf(line[0]) === -1) { fs_filter.push(line[0]); } + } + }); + + let output = fs_filter.join('|'); + exec('cat /proc/diskstats | egrep "' + output + '"', { maxBuffer: 1024 * 1024 }, function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + lines.forEach(function (line) { + line = line.trim(); + if (line !== '') { + line = line.replace(/ +/g, ' ').split(' '); + + rx += parseInt(line[5]) * 512; + wx += parseInt(line[9]) * 512; + } + }); + result = calcFsSpeed(rx, wx); + } + if (callback) { + callback(result); + } + resolve(result); + }); + } else { + if (callback) { + callback(result); + } + resolve(result); + } + }); + } + if (_darwin) { + 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) { + if (!error) { + let lines = stdout.toString().split('\n'); + lines.forEach(function (line) { + line = line.trim(); + if (line !== '') { + line = line.split(','); + + rx += parseInt(line[2]); + wx += parseInt(line[9]); + } + }); + result = calcFsSpeed(rx, wx); + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + } else { + result.ms = _fs_speed.last_ms; + result.rx = _fs_speed.bytes_read; + result.wx = _fs_speed.bytes_write; + result.tx = _fs_speed.bytes_read + _fs_speed.bytes_write; + result.rx_sec = _fs_speed.rx_sec; + result.wx_sec = _fs_speed.wx_sec; + result.tx_sec = _fs_speed.tx_sec; + if (callback) { + callback(result); + } + resolve(result); + } + }); + }); +} + +exports.fsStats = fsStats; + +function calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime) { + let result = { + rIO: 0, + wIO: 0, + tIO: 0, + rIO_sec: null, + wIO_sec: null, + tIO_sec: null, + rWaitTime: 0, + wWaitTime: 0, + tWaitTime: 0, + rWaitPercent: null, + wWaitPercent: null, + tWaitPercent: null, + ms: 0 + }; + if (_disk_io && _disk_io.ms) { + result.rIO = rIO; + result.wIO = wIO; + result.tIO = rIO + wIO; + result.ms = Date.now() - _disk_io.ms; + result.rIO_sec = (result.rIO - _disk_io.rIO) / (result.ms / 1000); + result.wIO_sec = (result.wIO - _disk_io.wIO) / (result.ms / 1000); + result.tIO_sec = result.rIO_sec + result.wIO_sec; + result.rWaitTime = rWaitTime; + result.wWaitTime = wWaitTime; + result.tWaitTime = tWaitTime; + result.rWaitPercent = (result.rWaitTime - _disk_io.rWaitTime) * 100 / (result.ms); + result.wWaitPercent = (result.wWaitTime - _disk_io.wWaitTime) * 100 / (result.ms); + result.tWaitPercent = (result.tWaitTime - _disk_io.tWaitTime) * 100 / (result.ms); + _disk_io.rIO = rIO; + _disk_io.wIO = wIO; + _disk_io.rIO_sec = result.rIO_sec; + _disk_io.wIO_sec = result.wIO_sec; + _disk_io.tIO_sec = result.tIO_sec; + _disk_io.rWaitTime = rWaitTime; + _disk_io.wWaitTime = wWaitTime; + _disk_io.tWaitTime = tWaitTime; + _disk_io.rWaitPercent = result.rWaitPercent; + _disk_io.wWaitPercent = result.wWaitPercent; + _disk_io.tWaitPercent = result.tWaitPercent; + _disk_io.last_ms = result.ms; + _disk_io.ms = Date.now(); + } else { + result.rIO = rIO; + result.wIO = wIO; + result.tIO = rIO + wIO; + result.rWaitTime = rWaitTime; + result.wWaitTime = wWaitTime; + result.tWaitTime = tWaitTime; + _disk_io.rIO = rIO; + _disk_io.wIO = wIO; + _disk_io.rIO_sec = null; + _disk_io.wIO_sec = null; + _disk_io.tIO_sec = null; + _disk_io.rWaitTime = rWaitTime; + _disk_io.wWaitTime = wWaitTime; + _disk_io.tWaitTime = tWaitTime; + _disk_io.rWaitPercent = null; + _disk_io.wWaitPercent = null; + _disk_io.tWaitPercent = null; + _disk_io.last_ms = 0; + _disk_io.ms = Date.now(); + } + return result; +} + +function disksIO(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + if (_windows) { + return resolve(null); + } + if (_sunos) { + return resolve(null); + } + + let result = { + rIO: 0, + wIO: 0, + tIO: 0, + rIO_sec: null, + wIO_sec: null, + tIO_sec: null, + rWaitTime: 0, + wWaitTime: 0, + tWaitTime: 0, + rWaitPercent: null, + wWaitPercent: null, + tWaitPercent: null, + ms: 0 + }; + let rIO = 0; + let wIO = 0; + let rWaitTime = 0; + let wWaitTime = 0; + let tWaitTime = 0; + + if ((_disk_io && !_disk_io.ms) || (_disk_io && _disk_io.ms && Date.now() - _disk_io.ms >= 500)) { + if (_linux || _freebsd || _openbsd || _netbsd) { + // prints Block layer statistics for all mounted volumes + // 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"; + // 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"; + 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'; + + exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) { + if (!error) { + let lines = stdout.split('\n'); + lines.forEach(function (line) { + // ignore empty lines + if (!line) { return; } + + // sum r/wIO of all disks to compute all disks IO + let stats = line.split(';'); + rIO += parseInt(stats[0]); + wIO += parseInt(stats[4]); + rWaitTime += parseInt(stats[3]); + wWaitTime += parseInt(stats[7]); + tWaitTime += parseInt(stats[10]); + }); + result = calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime); + + if (callback) { + callback(result); + } + resolve(result); + } else { + if (callback) { + callback(result); + } + resolve(result); + } + }); + } + if (_darwin) { + 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) { + if (!error) { + let lines = stdout.toString().split('\n'); + lines.forEach(function (line) { + line = line.trim(); + if (line !== '') { + line = line.split(','); + + rIO += parseInt(line[10]); + wIO += parseInt(line[0]); + } + }); + result = calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime); + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + } else { + result.rIO = _disk_io.rIO; + result.wIO = _disk_io.wIO; + result.tIO = _disk_io.rIO + _disk_io.wIO; + result.ms = _disk_io.last_ms; + result.rIO_sec = _disk_io.rIO_sec; + result.wIO_sec = _disk_io.wIO_sec; + result.tIO_sec = _disk_io.tIO_sec; + result.rWaitTime = _disk_io.rWaitTime; + result.wWaitTime = _disk_io.wWaitTime; + result.tWaitTime = _disk_io.tWaitTime; + result.rWaitPercent = _disk_io.rWaitPercent; + result.wWaitPercent = _disk_io.wWaitPercent; + result.tWaitPercent = _disk_io.tWaitPercent; + if (callback) { + callback(result); + } + resolve(result); + } + }); + }); +} + +exports.disksIO = disksIO; + +function diskLayout(callback) { + + function getVendorFromModel(model) { + const diskManufacturers = [ + { pattern: 'WESTERN.*', manufacturer: 'Western Digital' }, + { pattern: '^WDC.*', manufacturer: 'Western Digital' }, + { pattern: 'WD.*', manufacturer: 'Western Digital' }, + { pattern: 'TOSHIBA.*', manufacturer: 'Toshiba' }, + { pattern: 'HITACHI.*', manufacturer: 'Hitachi' }, + { pattern: '^IC.*', manufacturer: 'Hitachi' }, + { pattern: '^HTS.*', manufacturer: 'Hitachi' }, + { pattern: 'SANDISK.*', manufacturer: 'SanDisk' }, + { pattern: 'KINGSTON.*', manufacturer: 'Kingston Technology' }, + { pattern: '^SONY.*', manufacturer: 'Sony' }, + { pattern: 'TRANSCEND.*', manufacturer: 'Transcend' }, + { pattern: 'SAMSUNG.*', manufacturer: 'Samsung' }, + { pattern: '^ST(?!I\\ ).*', manufacturer: 'Seagate' }, + { pattern: '^STI\\ .*', manufacturer: 'SimpleTech' }, + { pattern: '^D...-.*', manufacturer: 'IBM' }, + { pattern: '^IBM.*', manufacturer: 'IBM' }, + { pattern: '^FUJITSU.*', manufacturer: 'Fujitsu' }, + { pattern: '^MP.*', manufacturer: 'Fujitsu' }, + { pattern: '^MK.*', manufacturer: 'Toshiba' }, + { pattern: 'MAXTO.*', manufacturer: 'Maxtor' }, + { pattern: 'PIONEER.*', manufacturer: 'Pioneer' }, + { pattern: 'PHILIPS.*', manufacturer: 'Philips' }, + { pattern: 'QUANTUM.*', manufacturer: 'Quantum Technology' }, + { pattern: 'FIREBALL.*', manufacturer: 'Quantum Technology' }, + { pattern: '^VBOX.*', manufacturer: 'VirtualBox' }, + { pattern: 'CORSAIR.*', manufacturer: 'Corsair Components' }, + { pattern: 'CRUCIAL.*', manufacturer: 'Crucial' }, + { pattern: 'ECM.*', manufacturer: 'ECM' }, + { pattern: 'INTEL.*', manufacturer: 'INTEL' }, + { pattern: 'EVO.*', manufacturer: 'Samsung' }, + { pattern: 'APPLE.*', manufacturer: 'Apple' }, + ]; + + let result = ''; + if (model) { + model = model.toUpperCase(); + diskManufacturers.forEach((manufacturer) => { + const re = RegExp(manufacturer.pattern); + if (re.test(model)) { result = manufacturer.manufacturer; } + }); + } + return result; + } + + return new Promise((resolve) => { + process.nextTick(() => { + + const commitResult = res => { + for (let i = 0; i < res.length; i++) { + delete res[i].BSDName; + } + if (callback) { + callback(res); + } + resolve(res); + }; + + let result = []; + let cmd = ''; + + if (_linux) { + let cmdFullSmart = ''; + + exec('export LC_ALL=C; lsblk -ablJO 2>/dev/null; unset LC_ALL', { maxBuffer: 1024 * 1024 }, function (error, stdout) { + if (!error) { + try { + const out = stdout.toString().trim(); + let devices = []; + try { + const outJSON = JSON.parse(out); + if (outJSON && {}.hasOwnProperty.call(outJSON, 'blockdevices')) { + 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 && item.path && item.path.indexOf('/ram') !== 0 && item.path.indexOf('/loop') !== 0 && item['disc-max'] && item['disc-max'] !== 0)); }); + } + } catch (e) { + // fallback to older version of lsblk + 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(); + let lines = blkStdoutToObject(out2).split('\n'); + const data = parseBlk(lines); + devices = data.filter(item => { return (item.type === 'disk') && item.size > 0 && ((item.model !== null && item.model !== '') || (item.mount === '' && item.label === '' && item.fsType === '')); }); + } + devices.forEach((device) => { + let mediumType = ''; + const BSDName = '/dev/' + device.name; + const logical = device.name; + try { + mediumType = execSync('cat /sys/block/' + logical + '/queue/rotational 2>/dev/null').toString().split('\n')[0]; + } catch (e) { + util.noop(); + } + let interfaceType = device.tran ? device.tran.toUpperCase().trim() : ''; + if (interfaceType === 'NVME') { + mediumType = '2'; + interfaceType = 'PCIe'; + } + result.push({ + device: BSDName, + 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'))))), + name: device.model || '', + vendor: getVendorFromModel(device.model) || (device.vendor ? device.vendor.trim() : ''), + size: device.size || 0, + bytesPerSector: null, + totalCylinders: null, + totalHeads: null, + totalSectors: null, + totalTracks: null, + tracksPerCylinder: null, + sectorsPerTrack: null, + firmwareRevision: device.rev ? device.rev.trim() : '', + serialNum: device.serial ? device.serial.trim() : '', + interfaceType: interfaceType, + smartStatus: 'unknown', + temperature: null, + BSDName: BSDName + }); + cmd += `printf "\n${BSDName}|"; smartctl -H ${BSDName} | grep overall;`; + cmdFullSmart += `${cmdFullSmart ? 'printf ",";' : ''}smartctl -a -j ${BSDName};`; + }); + } catch (e) { + util.noop(); + } + } + // check S.M.A.R.T. status + if (cmdFullSmart) { + exec(cmdFullSmart, { maxBuffer: 1024 * 1024 }, function (error, stdout) { + try { + const data = JSON.parse(`[${stdout}]`); + data.forEach(disk => { + const diskBSDName = disk.smartctl.argv[disk.smartctl.argv.length - 1]; + + for (let i = 0; i < result.length; i++) { + if (result[i].BSDName === diskBSDName) { + result[i].smartStatus = (disk.smart_status.passed ? 'Ok' : (disk.smart_status.passed === false ? 'Predicted Failure' : 'unknown')); + if (disk.temperature && disk.temperature.current) { + result[i].temperature = disk.temperature.current; + } + result[i].smartData = disk; + } + } + }); + commitResult(result); + } catch (e) { + if (cmd) { + cmd = cmd + 'printf "\n"'; + exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) { + let lines = stdout.toString().split('\n'); + lines.forEach(line => { + if (line) { + let parts = line.split('|'); + if (parts.length === 2) { + let BSDName = parts[0]; + parts[1] = parts[1].trim(); + let parts2 = parts[1].split(':'); + if (parts2.length === 2) { + parts2[1] = parts2[1].trim(); + let status = parts2[1].toLowerCase(); + for (let i = 0; i < result.length; i++) { + if (result[i].BSDName === BSDName) { + result[i].smartStatus = (status === 'passed' ? 'Ok' : (status === 'failed!' ? 'Predicted Failure' : 'unknown')); + } + } + } + } + } + }); + commitResult(result); + }); + } else { + commitResult(result); + } + } + }); + } else { + commitResult(result); + } + }); + } + if (_freebsd || _openbsd || _netbsd) { + if (callback) { callback(result); } + resolve(result); + } + if (_sunos) { + if (callback) { callback(result); } + resolve(result); + } + if (_darwin) { + exec('system_profiler SPSerialATADataType SPNVMeDataType SPUSBDataType', { maxBuffer: 1024 * 1024 }, function (error, stdout) { + if (!error) { + // split by type: + let lines = stdout.toString().split('\n'); + let linesSATA = []; + let linesNVMe = []; + let linesUSB = []; + let dataType = 'SATA'; + lines.forEach(line => { + if (line === 'NVMExpress:') { dataType = 'NVMe'; } + else if (line === 'USB:') { dataType = 'USB'; } + else if (line === 'SATA/SATA Express:') { dataType = 'SATA'; } + else if (dataType === 'SATA') { linesSATA.push(line); } + else if (dataType === 'NVMe') { linesNVMe.push(line); } + else if (dataType === 'USB') { linesUSB.push(line); } + }); + try { + // Serial ATA Drives + let devices = linesSATA.join('\n').split(' Physical Interconnect: '); + devices.shift(); + devices.forEach(function (device) { + device = 'InterfaceType: ' + device; + let lines = device.split('\n'); + const mediumType = util.getValue(lines, 'Medium Type', ':', true).trim(); + const sizeStr = util.getValue(lines, 'capacity', ':', true).trim(); + const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim(); + if (sizeStr) { + let sizeValue = 0; + if (sizeStr.indexOf('(') >= 0) { + sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, '').replace(/\s/g, '')); + } + if (!sizeValue) { + sizeValue = parseInt(sizeStr); + } + if (sizeValue) { + const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase(); + result.push({ + device: BSDName, + type: mediumType.startsWith('Solid') ? 'SSD' : 'HD', + name: util.getValue(lines, 'Model', ':', true).trim(), + vendor: getVendorFromModel(util.getValue(lines, 'Model', ':', true).trim()) || util.getValue(lines, 'Manufacturer', ':', true), + size: sizeValue, + bytesPerSector: null, + totalCylinders: null, + totalHeads: null, + totalSectors: null, + totalTracks: null, + tracksPerCylinder: null, + sectorsPerTrack: null, + firmwareRevision: util.getValue(lines, 'Revision', ':', true).trim(), + serialNum: util.getValue(lines, 'Serial Number', ':', true).trim(), + interfaceType: util.getValue(lines, 'InterfaceType', ':', true).trim(), + smartStatus: smartStatusString === 'verified' ? 'OK' : smartStatusString || 'unknown', + temperature: null, + BSDName: BSDName + }); + cmd = cmd + 'printf "\n' + BSDName + '|"; diskutil info /dev/' + BSDName + ' | grep SMART;'; + } + } + }); + } catch (e) { + util.noop(); + } + + // NVME Drives + try { + let devices = linesNVMe.join('\n').split('\n\n Capacity:'); + devices.shift(); + devices.forEach(function (device) { + device = '!Capacity: ' + device; + let lines = device.split('\n'); + const linkWidth = util.getValue(lines, 'link width', ':', true).trim(); + const sizeStr = util.getValue(lines, '!capacity', ':', true).trim(); + const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim(); + if (sizeStr) { + let sizeValue = 0; + if (sizeStr.indexOf('(') >= 0) { + sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, '').replace(/\s/g, '')); + } + if (!sizeValue) { + sizeValue = parseInt(sizeStr); + } + if (sizeValue) { + const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase(); + result.push({ + device: BSDName, + type: 'NVMe', + name: util.getValue(lines, 'Model', ':', true).trim(), + vendor: getVendorFromModel(util.getValue(lines, 'Model', ':', true).trim()), + size: sizeValue, + bytesPerSector: null, + totalCylinders: null, + totalHeads: null, + totalSectors: null, + totalTracks: null, + tracksPerCylinder: null, + sectorsPerTrack: null, + firmwareRevision: util.getValue(lines, 'Revision', ':', true).trim(), + serialNum: util.getValue(lines, 'Serial Number', ':', true).trim(), + interfaceType: ('PCIe ' + linkWidth).trim(), + smartStatus: smartStatusString === 'verified' ? 'OK' : smartStatusString || 'unknown', + temperature: null, + BSDName: BSDName + }); + cmd = cmd + 'printf "\n' + BSDName + '|"; diskutil info /dev/' + BSDName + ' | grep SMART;'; + } + } + }); + } catch (e) { + util.noop(); + } + // USB Drives + try { + let devices = linesUSB.join('\n').replaceAll('Media:\n ', 'Model:').split('\n\n Product ID:'); + devices.shift(); + devices.forEach(function (device) { + let lines = device.split('\n'); + const sizeStr = util.getValue(lines, 'Capacity', ':', true).trim(); + const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim(); + if (sizeStr) { + let sizeValue = 0; + if (sizeStr.indexOf('(') >= 0) { + sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, '').replace(/\s/g, '')); + } + if (!sizeValue) { + sizeValue = parseInt(sizeStr); + } + if (sizeValue) { + const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase(); + result.push({ + device: BSDName, + type: 'USB', + name: util.getValue(lines, 'Model', ':', true).trim().replaceAll(':', ''), + vendor: getVendorFromModel(util.getValue(lines, 'Model', ':', true).trim()), + size: sizeValue, + bytesPerSector: null, + totalCylinders: null, + totalHeads: null, + totalSectors: null, + totalTracks: null, + tracksPerCylinder: null, + sectorsPerTrack: null, + firmwareRevision: util.getValue(lines, 'Revision', ':', true).trim(), + serialNum: util.getValue(lines, 'Serial Number', ':', true).trim(), + interfaceType: 'USB', + smartStatus: smartStatusString === 'verified' ? 'OK' : smartStatusString || 'unknown', + temperature: null, + BSDName: BSDName + }); + cmd = cmd + 'printf "\n' + BSDName + '|"; diskutil info /dev/' + BSDName + ' | grep SMART;'; + } + } + }); + } catch (e) { + util.noop(); + } + if (cmd) { + cmd = cmd + 'printf "\n"'; + exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) { + let lines = stdout.toString().split('\n'); + lines.forEach(line => { + if (line) { + let parts = line.split('|'); + if (parts.length === 2) { + let BSDName = parts[0]; + parts[1] = parts[1].trim(); + let parts2 = parts[1].split(':'); + if (parts2.length === 2) { + parts2[1] = parts2[1].trim(); + let status = parts2[1].toLowerCase(); + for (let i = 0; i < result.length; i++) { + if (result[i].BSDName === BSDName) { + result[i].smartStatus = (status === 'not supported' ? 'not supported' : (status === 'verified' ? 'Ok' : (status === 'failing' ? 'Predicted Failure' : 'unknown'))); + } + } + } + } + } + }); + for (let i = 0; i < result.length; i++) { + delete result[i].BSDName; + } + if (callback) { + callback(result); + } + resolve(result); + }); + } else { + for (let i = 0; i < result.length; i++) { + delete result[i].BSDName; + } + if (callback) { + callback(result); + } + resolve(result); + } + } + }); + } + if (_windows) { + try { + const workload = []; + workload.push(util.powerShell('Get-WmiObject Win32_DiskDrive | select Caption,Size,Status,PNPDeviceId,BytesPerSector,TotalCylinders,TotalHeads,TotalSectors,TotalTracks,TracksPerCylinder,SectorsPerTrack,FirmwareRevision,SerialNumber,InterfaceType | fl')); + workload.push(util.powerShell('Get-PhysicalDisk | select BusType,MediaType,FriendlyName,Model,SerialNumber,Size | fl')); + if (util.smartMonToolsInstalled()) { + try { + const smartDev = JSON.parse(execSync('smartctl --scan -j')); + if (smartDev && smartDev.devices && smartDev.devices.length > 0) { + smartDev.devices.forEach((dev) => { + workload.push(execPromiseSave(`smartctl -j -a ${dev.name}`, util.execOptsWin)); + }); + } + } catch (e) { + util.noop(); + } + } + util.promiseAll( + workload + ).then(data => { + let devices = data.results[0].toString().split(/\n\s*\n/); + devices.forEach(function (device) { + let lines = device.split('\r\n'); + const size = util.getValue(lines, 'Size', ':').trim(); + const status = util.getValue(lines, 'Status', ':').trim().toLowerCase(); + if (size) { + result.push({ + device: util.getValue(lines, 'PNPDeviceId', ':'), + type: device.indexOf('SSD') > -1 ? 'SSD' : 'HD', // just a starting point ... better: MSFT_PhysicalDisk - Media Type ... see below + name: util.getValue(lines, 'Caption', ':'), + vendor: getVendorFromModel(util.getValue(lines, 'Caption', ':', true).trim()), + size: parseInt(size), + bytesPerSector: parseInt(util.getValue(lines, 'BytesPerSector', ':')), + totalCylinders: parseInt(util.getValue(lines, 'TotalCylinders', ':')), + totalHeads: parseInt(util.getValue(lines, 'TotalHeads', ':')), + totalSectors: parseInt(util.getValue(lines, 'TotalSectors', ':')), + totalTracks: parseInt(util.getValue(lines, 'TotalTracks', ':')), + tracksPerCylinder: parseInt(util.getValue(lines, 'TracksPerCylinder', ':')), + sectorsPerTrack: parseInt(util.getValue(lines, 'SectorsPerTrack', ':')), + firmwareRevision: util.getValue(lines, 'FirmwareRevision', ':').trim(), + serialNum: util.getValue(lines, 'SerialNumber', ':').trim(), + interfaceType: util.getValue(lines, 'InterfaceType', ':').trim(), + smartStatus: (status === 'ok' ? 'Ok' : (status === 'degraded' ? 'Degraded' : (status === 'pred fail' ? 'Predicted Failure' : 'Unknown'))), + temperature: null, + }); + } + }); + devices = data.results[1].split(/\n\s*\n/); + devices.forEach(function (device) { + let lines = device.split('\r\n'); + const serialNum = util.getValue(lines, 'SerialNumber', ':').trim(); + const name = util.getValue(lines, 'FriendlyName', ':').trim().replace('Msft ', 'Microsoft'); + const size = util.getValue(lines, 'Size', ':').trim(); + const model = util.getValue(lines, 'Model', ':').trim(); + const interfaceType = util.getValue(lines, 'BusType', ':').trim(); + let mediaType = util.getValue(lines, 'MediaType', ':').trim(); + if (mediaType === '3' || mediaType === 'HDD') { mediaType = 'HD'; } + if (mediaType === '4') { mediaType = 'SSD'; } + if (mediaType === '5') { mediaType = 'SCM'; } + if (mediaType === 'Unspecified' && (model.toLowerCase().indexOf('virtual') > -1 || model.toLowerCase().indexOf('vbox') > -1)) { mediaType = 'Virtual'; } + if (size) { + let i = util.findObjectByKey(result, 'serialNum', serialNum); + if (i === -1 || serialNum === '') { + i = util.findObjectByKey(result, 'name', name); + } + if (i != -1) { + result[i].type = mediaType; + result[i].interfaceType = interfaceType; + } + } + }); + // S.M.A.R.T + data.results.shift(); + data.results.shift(); + if (data.results.length) { + data.results.forEach((smartStr) => { + try { + const smartData = JSON.parse(smartStr); + if (smartData.serial_number) { + const serialNum = smartData.serial_number; + let i = util.findObjectByKey(result, 'serialNum', serialNum); + if (i != -1) { + result[i].smartStatus = (smartData.smart_status && smartData.smart_status.passed ? 'Ok' : (smartData.smart_status && smartData.smart_status.passed === false ? 'Predicted Failure' : 'unknown')); + if (smartData.temperature && smartData.temperature.current) { + result[i].temperature = smartData.temperature.current; + } + result[i].smartData = smartData; + } + } + } catch (e) { + util.noop(); + } + }); + } + if (callback) { + callback(result); + } + resolve(result); + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + }); + }); +} + +exports.diskLayout = diskLayout; diff --git a/node_modules/systeminformation/lib/graphics.js b/node_modules/systeminformation/lib/graphics.js new file mode 100644 index 0000000..c904e86 --- /dev/null +++ b/node_modules/systeminformation/lib/graphics.js @@ -0,0 +1,1062 @@ +'use strict'; +// @ts-check +// ================================================================================== +// graphics.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 7. Graphics (controller, display) +// ---------------------------------------------------------------------------------- + +const fs = require('fs'); +const exec = require('child_process').exec; +const execSync = require('child_process').execSync; +const util = require('./util'); + +let _platform = process.platform; +let _nvidiaSmiPath = ''; + +const _linux = (_platform === 'linux' || _platform === 'android'); +const _darwin = (_platform === 'darwin'); +const _windows = (_platform === 'win32'); +const _freebsd = (_platform === 'freebsd'); +const _openbsd = (_platform === 'openbsd'); +const _netbsd = (_platform === 'netbsd'); +const _sunos = (_platform === 'sunos'); + +let _resolutionX = 0; +let _resolutionY = 0; +let _pixelDepth = 0; +let _refreshRate = 0; + +const videoTypes = { + '-2': 'UNINITIALIZED', + '-1': 'OTHER', + '0': 'HD15', + '1': 'SVIDEO', + '2': 'Composite video', + '3': 'Component video', + '4': 'DVI', + '5': 'HDMI', + '6': 'LVDS', + '8': 'D_JPN', + '9': 'SDI', + '10': 'DP', + '11': 'DP embedded', + '12': 'UDI', + '13': 'UDI embedded', + '14': 'SDTVDONGLE', + '15': 'MIRACAST', + '2147483648': 'INTERNAL' +}; + +function getVendorFromModel(model) { + const manufacturers = [ + { pattern: '^LG.+', manufacturer: 'LG' }, + { pattern: '^BENQ.+', manufacturer: 'BenQ' }, + { pattern: '^ASUS.+', manufacturer: 'Asus' }, + { pattern: '^DELL.+', manufacturer: 'Dell' }, + { pattern: '^SAMSUNG.+', manufacturer: 'Samsung' }, + { pattern: '^VIEWSON.+', manufacturer: 'ViewSonic' }, + { pattern: '^SONY.+', manufacturer: 'Sony' }, + { pattern: '^ACER.+', manufacturer: 'Acer' }, + { pattern: '^AOC.+', manufacturer: 'AOC Monitors' }, + { pattern: '^HP.+', manufacturer: 'HP' }, + { pattern: '^EIZO.?', manufacturer: 'Eizo' }, + { pattern: '^PHILIPS.?', manufacturer: 'Philips' }, + { pattern: '^IIYAMA.?', manufacturer: 'Iiyama' }, + { pattern: '^SHARP.?', manufacturer: 'Sharp' }, + { pattern: '^NEC.?', manufacturer: 'NEC' }, + { pattern: '^LENOVO.?', manufacturer: 'Lenovo' }, + { pattern: 'COMPAQ.?', manufacturer: 'Compaq' }, + { pattern: 'APPLE.?', manufacturer: 'Apple' }, + { pattern: 'INTEL.?', manufacturer: 'Intel' }, + { pattern: 'AMD.?', manufacturer: 'AMD' }, + { pattern: 'NVIDIA.?', manufacturer: 'NVDIA' }, + ]; + + let result = ''; + if (model) { + model = model.toUpperCase(); + manufacturers.forEach((manufacturer) => { + const re = RegExp(manufacturer.pattern); + if (re.test(model)) { result = manufacturer.manufacturer; } + }); + } + return result; +} + +function getVendorFromId(id) { + const vendors = { + '610': 'Apple', + '1e6d': 'LG', + '10ac': 'DELL', + '4dd9': 'Sony', + '38a3': 'NEC', + }; + return vendors[id] || ''; +} + +function vendorToId(str) { + let result = ''; + str = (str || '').toLowerCase(); + if (str.indexOf('apple') >= 0) { result = '0x05ac'; } + else if (str.indexOf('nvidia') >= 0) { result = '0x10de'; } + else if (str.indexOf('intel') >= 0) { result = '0x8086'; } + else if (str.indexOf('ati') >= 0 || str.indexOf('amd') >= 0) { result = '0x1002'; } + + return result; +} + +function getMetalVersion(id) { + const families = { + 'spdisplays_mtlgpufamilymac1': 'mac1', + 'spdisplays_mtlgpufamilymac2': 'mac2', + 'spdisplays_mtlgpufamilyapple1': 'apple1', + 'spdisplays_mtlgpufamilyapple2': 'apple2', + 'spdisplays_mtlgpufamilyapple3': 'apple3', + 'spdisplays_mtlgpufamilyapple4': 'apple4', + 'spdisplays_mtlgpufamilyapple5': 'apple5', + 'spdisplays_mtlgpufamilyapple6': 'apple6', + 'spdisplays_mtlgpufamilyapple7': 'apple7', + 'spdisplays_metalfeaturesetfamily11': 'family1_v1', + 'spdisplays_metalfeaturesetfamily12': 'family1_v2', + 'spdisplays_metalfeaturesetfamily13': 'family1_v3', + 'spdisplays_metalfeaturesetfamily14': 'family1_v4', + 'spdisplays_metalfeaturesetfamily21': 'family2_v1' + }; + return families[id] || ''; +} + +function graphics(callback) { + + function parseLinesDarwin(graphicsArr) { + const res = { + controllers: [], + displays: [] + }; + try { + graphicsArr.forEach(function (item) { + // controllers + const bus = ((item.sppci_bus || '').indexOf('builtin') > -1 ? 'Built-In' : ((item.sppci_bus || '').indexOf('pcie') > -1 ? 'PCIe' : '')); + const vram = (parseInt((item.spdisplays_vram || ''), 10) || 0) * (((item.spdisplays_vram || '').indexOf('GB') > -1) ? 1024 : 1); + const vramDyn = (parseInt((item.spdisplays_vram_shared || ''), 10) || 0) * (((item.spdisplays_vram_shared || '').indexOf('GB') > -1) ? 1024 : 1); + let metalVersion = getMetalVersion(item.spdisplays_metal || item.spdisplays_metalfamily || ''); + res.controllers.push({ + vendor: getVendorFromModel(item.spdisplays_vendor || '') || item.spdisplays_vendor || '', + model: item.sppci_model || '', + bus, + vramDynamic: bus === 'Built-In', + vram: vram || vramDyn || null, + deviceId: item['spdisplays_device-id'] || '', + vendorId: item['spdisplays_vendor-id'] || vendorToId((item['spdisplays_vendor'] || '') + (item.sppci_model || '')), + external: (item.sppci_device_type === 'spdisplays_egpu'), + cores: item['sppci_cores'] || null, + metalVersion + }); + + // displays + if (item.spdisplays_ndrvs && item.spdisplays_ndrvs.length) { + item.spdisplays_ndrvs.forEach(function (displayItem) { + const connectionType = displayItem['spdisplays_connection_type'] || ''; + const currentResolutionParts = (displayItem['_spdisplays_resolution'] || '').split('@'); + const currentResolution = currentResolutionParts[0].split('x'); + const pixelParts = (displayItem['_spdisplays_pixels'] || '').split('x'); + const pixelDepthString = displayItem['spdisplays_depth'] || ''; + const serial = displayItem['_spdisplays_display-serial-number'] || displayItem['_spdisplays_display-serial-number2'] || null; + res.displays.push({ + vendor: getVendorFromId(displayItem['_spdisplays_display-vendor-id'] || '') || getVendorFromModel(displayItem['_name'] || ''), + vendorId: displayItem['_spdisplays_display-vendor-id'] || '', + model: displayItem['_name'] || '', + productionYear: displayItem['_spdisplays_display-year'] || null, + serial: serial !== '0' ? serial : null, + displayId: displayItem['_spdisplays_displayID'] || null, + main: displayItem['spdisplays_main'] ? displayItem['spdisplays_main'] === 'spdisplays_yes' : false, + builtin: (displayItem['spdisplays_display_type'] || '').indexOf('built-in') > -1, + connection: ((connectionType.indexOf('_internal') > -1) ? 'Internal' : ((connectionType.indexOf('_displayport') > -1) ? 'Display Port' : ((connectionType.indexOf('_hdmi') > -1) ? 'HDMI' : null))), + sizeX: null, + sizeY: null, + pixelDepth: (pixelDepthString === 'CGSThirtyBitColor' ? 30 : (pixelDepthString === 'CGSThirtytwoBitColor' ? 32 : (pixelDepthString === 'CGSTwentyfourBitColor' ? 24 : null))), + resolutionX: pixelParts.length > 1 ? parseInt(pixelParts[0], 10) : null, + resolutionY: pixelParts.length > 1 ? parseInt(pixelParts[1], 10) : null, + currentResX: currentResolution.length > 1 ? parseInt(currentResolution[0], 10) : null, + currentResY: currentResolution.length > 1 ? parseInt(currentResolution[1], 10) : null, + positionX: 0, + positionY: 0, + currentRefreshRate: currentResolutionParts.length > 1 ? parseInt(currentResolutionParts[1], 10) : null, + + }); + }); + } + }); + return res; + } catch (e) { + return res; + } + } + + function parseLinesLinuxControllers(lines) { + let controllers = []; + let currentController = { + vendor: '', + model: '', + bus: '', + busAddress: '', + vram: null, + vramDynamic: false, + pciID: '' + }; + let isGraphicsController = false; + // PCI bus IDs + let pciIDs = []; + try { + pciIDs = execSync('export LC_ALL=C; dmidecode -t 9 2>/dev/null; unset LC_ALL | grep "Bus Address: "').toString().split('\n'); + for (let i = 0; i < pciIDs.length; i++) { + pciIDs[i] = pciIDs[i].replace('Bus Address:', '').replace('0000:', '').trim(); + } + pciIDs = pciIDs.filter(function (el) { + return el != null && el; + }); + } catch (e) { + util.noop(); + } + for (let i = 0; i < lines.length; i++) { + if ('' !== lines[i].trim()) { + if (' ' !== lines[i][0] && '\t' !== lines[i][0]) { // first line of new entry + let isExternal = (pciIDs.indexOf(lines[i].split(' ')[0]) >= 0); + let vgapos = lines[i].toLowerCase().indexOf(' vga '); + let _3dcontrollerpos = lines[i].toLowerCase().indexOf('3d controller'); + if (vgapos !== -1 || _3dcontrollerpos !== -1) { // VGA + if (_3dcontrollerpos !== -1 && vgapos === -1) { + vgapos = _3dcontrollerpos; + } + if (currentController.vendor || currentController.model || currentController.bus || currentController.vram !== null || currentController.vramDynamic) { // already a controller found + controllers.push(currentController); + currentController = { + vendor: '', + model: '', + bus: '', + busAddress: '', + vram: null, + vramDynamic: false, + }; + } + + const pciIDCandidate = lines[i].split(' ')[0]; + if (/[\da-fA-F]{2}:[\da-fA-F]{2}\.[\da-fA-F]/.test(pciIDCandidate)) { + currentController.busAddress = pciIDCandidate; + } + isGraphicsController = true; + let endpos = lines[i].search(/\[[0-9a-f]{4}:[0-9a-f]{4}]|$/); + let parts = lines[i].substr(vgapos, endpos - vgapos).split(':'); + currentController.busAddress = lines[i].substr(0, vgapos).trim(); + if (parts.length > 1) { + parts[1] = parts[1].trim(); + if (parts[1].toLowerCase().indexOf('corporation') >= 0) { + currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf('corporation') + 11).trim(); + currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf('corporation') + 11, 200).trim().split('(')[0]; + currentController.bus = (pciIDs.length > 0 && isExternal) ? 'PCIe' : 'Onboard'; + currentController.vram = null; + currentController.vramDynamic = false; + } else if (parts[1].toLowerCase().indexOf(' inc.') >= 0) { + if ((parts[1].match(new RegExp(']', 'g')) || []).length > 1) { + currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(']') + 1).trim(); + currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(']') + 1, 200).trim().split('(')[0].trim(); + } else { + currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(' inc.') + 5).trim(); + currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(' inc.') + 5, 200).trim().split('(')[0].trim(); + } + currentController.bus = (pciIDs.length > 0 && isExternal) ? 'PCIe' : 'Onboard'; + currentController.vram = null; + currentController.vramDynamic = false; + } else if (parts[1].toLowerCase().indexOf(' ltd.') >= 0) { + if ((parts[1].match(new RegExp(']', 'g')) || []).length > 1) { + currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(']') + 1).trim(); + currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(']') + 1, 200).trim().split('(')[0].trim(); + } else { + currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(' ltd.') + 5).trim(); + currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(' ltd.') + 5, 200).trim().split('(')[0].trim(); + } + } + } + + } else { + isGraphicsController = false; + } + } + if (isGraphicsController) { // within VGA details + let parts = lines[i].split(':'); + if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('devicename') !== -1 && parts[1].toLowerCase().indexOf('onboard') !== -1) { currentController.bus = 'Onboard'; } + if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('region') !== -1 && parts[1].toLowerCase().indexOf('memory') !== -1) { + let memparts = parts[1].split('='); + if (memparts.length > 1) { + currentController.vram = parseInt(memparts[1]); + } + } + } + } + } + if (currentController.vendor || currentController.model || currentController.bus || currentController.busAddress || currentController.vram !== null || currentController.vramDynamic) { // already a controller found + controllers.push(currentController); + } + return (controllers); + } + + function parseLinesLinuxClinfo(controllers, lines) { + const fieldPattern = /\[([^\]]+)\]\s+(\w+)\s+(.*)/; + const devices = lines.reduce((devices, line) => { + const field = fieldPattern.exec(line.trim()); + if (field) { + if (!devices[field[1]]) { + devices[field[1]] = {}; + } + devices[field[1]][field[2]] = field[3]; + } + return devices; + }, {}); + for (let deviceId in devices) { + const device = devices[deviceId]; + if (device['CL_DEVICE_TYPE'] === 'CL_DEVICE_TYPE_GPU') { + let busAddress; + if (device['CL_DEVICE_TOPOLOGY_AMD']) { + const bdf = device['CL_DEVICE_TOPOLOGY_AMD'].match(/[a-zA-Z0-9]+:\d+\.\d+/); + if (bdf) { + busAddress = bdf[0]; + } + } else if (device['CL_DEVICE_PCI_BUS_ID_NV'] && device['CL_DEVICE_PCI_SLOT_ID_NV']) { + const bus = parseInt(device['CL_DEVICE_PCI_BUS_ID_NV']); + const slot = parseInt(device['CL_DEVICE_PCI_SLOT_ID_NV']); + if (!isNaN(bus) && !isNaN(slot)) { + const b = bus & 0xff; + const d = (slot >> 3) & 0xff; + const f = slot & 0x07; + busAddress = `${b.toString().padStart(2, '0')}:${d.toString().padStart(2, '0')}.${f}`; + } + } + if (busAddress) { + let controller = controllers.find(controller => controller.busAddress === busAddress); + if (!controller) { + controller = { + vendor: '', + model: '', + bus: '', + busAddress, + vram: null, + vramDynamic: false + }; + controllers.push(controller); + } + controller.vendor = device['CL_DEVICE_VENDOR']; + if (device['CL_DEVICE_BOARD_NAME_AMD']) { + controller.model = device['CL_DEVICE_BOARD_NAME_AMD']; + } else { + controller.model = device['CL_DEVICE_NAME']; + } + const memory = parseInt(device['CL_DEVICE_GLOBAL_MEM_SIZE']); + if (!isNaN(memory)) { + controller.vram = Math.round(memory / 1024 / 1024); + } + } + } + } + return controllers; + } + + function getNvidiaSmi() { + if (_nvidiaSmiPath) { + return _nvidiaSmiPath; + } + + if (_windows) { + try { + const basePath = util.WINDIR + '\\System32\\DriverStore\\FileRepository'; + // find all directories that have an nvidia-smi.exe file + const candidateDirs = fs.readdirSync(basePath).filter(dir => { + return fs.readdirSync([basePath, dir].join('/')).includes('nvidia-smi.exe'); + }); + // use the directory with the most recently created nvidia-smi.exe file + const targetDir = candidateDirs.reduce((prevDir, currentDir) => { + const previousNvidiaSmi = fs.statSync([basePath, prevDir, 'nvidia-smi.exe'].join('/')); + const currentNvidiaSmi = fs.statSync([basePath, currentDir, 'nvidia-smi.exe'].join('/')); + return (previousNvidiaSmi.ctimeMs > currentNvidiaSmi.ctimeMs) ? prevDir : currentDir; + }); + + if (targetDir) { + _nvidiaSmiPath = [basePath, targetDir, 'nvidia-smi.exe'].join('/'); + } + } catch (e) { + util.noop(); + } + } else if (_linux) { + _nvidiaSmiPath = 'nvidia-smi'; + } + return _nvidiaSmiPath; + } + + function nvidiaSmi(options) { + const nvidiaSmiExe = getNvidiaSmi(); + options = options || util.execOptsWin; + if (nvidiaSmiExe) { + const nvidiaSmiOpts = '--query-gpu=driver_version,pci.sub_device_id,name,pci.bus_id,fan.speed,memory.total,memory.used,memory.free,utilization.gpu,utilization.memory,temperature.gpu,temperature.memory,power.draw,power.limit,clocks.gr,clocks.mem --format=csv,noheader,nounits'; + const cmd = nvidiaSmiExe + ' ' + nvidiaSmiOpts + (_linux ? ' 2>/dev/null' : ''); + try { + const res = execSync(cmd, options).toString(); + return res; + } catch (e) { + util.noop(); + } + } + return ''; + } + + function nvidiaDevices() { + + function safeParseNumber(value) { + if ([null, undefined].includes(value)) { + return value; + } + return parseFloat(value); + } + + const stdout = nvidiaSmi(); + if (!stdout) { + return []; + } + + const gpus = stdout.split('\n').filter(Boolean); + const results = gpus.map(gpu => { + const splittedData = gpu.split(', ').map(value => value.includes('N/A') ? undefined : value); + if (splittedData.length === 16) { + return { + driverVersion: splittedData[0], + subDeviceId: splittedData[1], + name: splittedData[2], + pciBus: splittedData[3], + fanSpeed: safeParseNumber(splittedData[4]), + memoryTotal: safeParseNumber(splittedData[5]), + memoryUsed: safeParseNumber(splittedData[6]), + memoryFree: safeParseNumber(splittedData[7]), + utilizationGpu: safeParseNumber(splittedData[8]), + utilizationMemory: safeParseNumber(splittedData[9]), + temperatureGpu: safeParseNumber(splittedData[10]), + temperatureMemory: safeParseNumber(splittedData[11]), + powerDraw: safeParseNumber(splittedData[12]), + powerLimit: safeParseNumber(splittedData[13]), + clockCore: safeParseNumber(splittedData[14]), + clockMemory: safeParseNumber(splittedData[15]), + }; + } + }); + + return results; + } + + function mergeControllerNvidia(controller, nvidia) { + if (nvidia.driverVersion) { controller.driverVersion = nvidia.driverVersion; } + if (nvidia.subDeviceId) { controller.subDeviceId = nvidia.subDeviceId; } + if (nvidia.name) { controller.name = nvidia.name; } + if (nvidia.pciBus) { controller.pciBus = nvidia.pciBus; } + if (nvidia.fanSpeed) { controller.fanSpeed = nvidia.fanSpeed; } + if (nvidia.memoryTotal) { + controller.memoryTotal = nvidia.memoryTotal; + controller.vram = nvidia.memoryTotal; + controller.vramDynamic = false; + } + if (nvidia.memoryUsed) { controller.memoryUsed = nvidia.memoryUsed; } + if (nvidia.memoryFree) { controller.memoryFree = nvidia.memoryFree; } + if (nvidia.utilizationGpu) { controller.utilizationGpu = nvidia.utilizationGpu; } + if (nvidia.utilizationMemory) { controller.utilizationMemory = nvidia.utilizationMemory; } + if (nvidia.temperatureGpu) { controller.temperatureGpu = nvidia.temperatureGpu; } + if (nvidia.temperatureMemory) { controller.temperatureMemory = nvidia.temperatureMemory; } + if (nvidia.powerDraw) { controller.powerDraw = nvidia.powerDraw; } + if (nvidia.powerLimit) { controller.powerLimit = nvidia.powerLimit; } + if (nvidia.clockCore) { controller.clockCore = nvidia.clockCore; } + if (nvidia.clockMemory) { controller.clockMemory = nvidia.clockMemory; } + return controller; + } + + function parseLinesLinuxEdid(edid) { + // parsen EDID + // --> model + // --> resolutionx + // --> resolutiony + // --> builtin = false + // --> pixeldepth (?) + // --> sizex + // --> sizey + let result = { + vendor: '', + model: '', + deviceName: '', + main: false, + builtin: false, + connection: '', + sizeX: null, + sizeY: null, + pixelDepth: null, + resolutionX: null, + resolutionY: null, + currentResX: null, + currentResY: null, + positionX: 0, + positionY: 0, + currentRefreshRate: null + }; + // find first "Detailed Timing Description" + let start = 108; + if (edid.substr(start, 6) === '000000') { + start += 36; + } + if (edid.substr(start, 6) === '000000') { + start += 36; + } + if (edid.substr(start, 6) === '000000') { + start += 36; + } + if (edid.substr(start, 6) === '000000') { + start += 36; + } + result.resolutionX = parseInt('0x0' + edid.substr(start + 8, 1) + edid.substr(start + 4, 2)); + result.resolutionY = parseInt('0x0' + edid.substr(start + 14, 1) + edid.substr(start + 10, 2)); + result.sizeX = parseInt('0x0' + edid.substr(start + 28, 1) + edid.substr(start + 24, 2)); + result.sizeY = parseInt('0x0' + edid.substr(start + 29, 1) + edid.substr(start + 26, 2)); + // monitor name + start = edid.indexOf('000000fc00'); // find first "Monitor Description Data" + if (start >= 0) { + let model_raw = edid.substr(start + 10, 26); + if (model_raw.indexOf('0a') !== -1) { + model_raw = model_raw.substr(0, model_raw.indexOf('0a')); + } + try { + if (model_raw.length > 2) { + result.model = model_raw.match(/.{1,2}/g).map(function (v) { + return String.fromCharCode(parseInt(v, 16)); + }).join(''); + } + } catch (e) { + util.noop(); + } + } else { + result.model = ''; + } + return result; + } + + function parseLinesLinuxDisplays(lines, depth) { + let displays = []; + let currentDisplay = { + vendor: '', + model: '', + deviceName: '', + main: false, + builtin: false, + connection: '', + sizeX: null, + sizeY: null, + pixelDepth: null, + resolutionX: null, + resolutionY: null, + currentResX: null, + currentResY: null, + positionX: 0, + positionY: 0, + currentRefreshRate: null + }; + let is_edid = false; + let is_current = false; + let edid_raw = ''; + let start = 0; + for (let i = 1; i < lines.length; i++) { // start with second line + if ('' !== lines[i].trim()) { + if (' ' !== lines[i][0] && '\t' !== lines[i][0] && lines[i].toLowerCase().indexOf(' connected ') !== -1) { // first line of new entry + if (currentDisplay.model || currentDisplay.main || currentDisplay.builtin || currentDisplay.connection || currentDisplay.sizeX !== null || currentDisplay.pixelDepth !== null || currentDisplay.resolutionX !== null) { // push last display to array + displays.push(currentDisplay); + currentDisplay = { + vendor: '', + model: '', + main: false, + builtin: false, + connection: '', + sizeX: null, + sizeY: null, + pixelDepth: null, + resolutionX: null, + resolutionY: null, + currentResX: null, + currentResY: null, + positionX: 0, + positionY: 0, + currentRefreshRate: null + }; + } + let parts = lines[i].split(' '); + currentDisplay.connection = parts[0]; + currentDisplay.main = lines[i].toLowerCase().indexOf(' primary ') >= 0; + currentDisplay.builtin = (parts[0].toLowerCase().indexOf('edp') >= 0); + } + + // try to read EDID information + if (is_edid) { + if (lines[i].search(/\S|$/) > start) { + edid_raw += lines[i].toLowerCase().trim(); + } else { + // parsen EDID + let edid_decoded = parseLinesLinuxEdid(edid_raw); + currentDisplay.vendor = edid_decoded.vendor; + currentDisplay.model = edid_decoded.model; + currentDisplay.resolutionX = edid_decoded.resolutionX; + currentDisplay.resolutionY = edid_decoded.resolutionY; + currentDisplay.sizeX = edid_decoded.sizeX; + currentDisplay.sizeY = edid_decoded.sizeY; + currentDisplay.pixelDepth = depth; + is_edid = false; + } + } + if (lines[i].toLowerCase().indexOf('edid:') >= 0) { + is_edid = true; + start = lines[i].search(/\S|$/); + } + if (lines[i].toLowerCase().indexOf('*current') >= 0) { + const parts1 = lines[i].split('('); + if (parts1 && parts1.length > 1 && parts1[0].indexOf('x') >= 0) { + const resParts = parts1[0].trim().split('x'); + currentDisplay.currentResX = util.toInt(resParts[0]); + currentDisplay.currentResY = util.toInt(resParts[1]); + } + is_current = true; + } + if (is_current && lines[i].toLowerCase().indexOf('clock') >= 0 && lines[i].toLowerCase().indexOf('hz') >= 0 && lines[i].toLowerCase().indexOf('v: height') >= 0) { + const parts1 = lines[i].split('clock'); + if (parts1 && parts1.length > 1 && parts1[1].toLowerCase().indexOf('hz') >= 0) { + currentDisplay.currentRefreshRate = util.toInt(parts1[1]); + } + is_current = false; + } + } + } + + // pushen displays + if (currentDisplay.model || currentDisplay.main || currentDisplay.builtin || currentDisplay.connection || currentDisplay.sizeX !== null || currentDisplay.pixelDepth !== null || currentDisplay.resolutionX !== null) { // still information there + displays.push(currentDisplay); + } + return displays; + } + + // function starts here + return new Promise((resolve) => { + process.nextTick(() => { + let result = { + controllers: [], + displays: [] + }; + if (_darwin) { + let cmd = 'system_profiler -xml -detailLevel full SPDisplaysDataType'; + exec(cmd, function (error, stdout) { + if (!error) { + try { + let output = stdout.toString(); + result = parseLinesDarwin(util.plistParser(output)[0]._items); + } catch (e) { + util.noop(); + } + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + if (_linux) { + // Raspberry: https://elinux.org/RPI_vcgencmd_usage + if (util.isRaspberry() && util.isRaspbian()) { + let cmd = 'fbset -s | grep \'mode "\'; vcgencmd get_mem gpu; tvservice -s; tvservice -n;'; + exec(cmd, function (error, stdout) { + let lines = stdout.toString().split('\n'); + if (lines.length > 3 && lines[0].indexOf('mode "') >= -1 && lines[2].indexOf('0x12000a') > -1) { + const parts = lines[0].replace('mode', '').replace(/"/g, '').trim().split('x'); + if (parts.length === 2) { + result.displays.push({ + vendor: '', + model: util.getValue(lines, 'device_name', '='), + main: true, + builtin: false, + connection: 'HDMI', + sizeX: null, + sizeY: null, + pixelDepth: null, + resolutionX: parseInt(parts[0], 10), + resolutionY: parseInt(parts[1], 10), + currentResX: null, + currentResY: null, + positionX: 0, + positionY: 0, + currentRefreshRate: null + }); + } + } + if (lines.length > 1 && stdout.toString().indexOf('gpu=') >= -1) { + result.controllers.push({ + vendor: 'Broadcom', + model: 'VideoCore IV', + bus: '', + vram: util.getValue(lines, 'gpu', '=').replace('M', ''), + vramDynamic: true + }); + } + if (callback) { + callback(result); + } + resolve(result); + }); + } else { + let cmd = 'lspci -vvv 2>/dev/null'; + exec(cmd, function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + result.controllers = parseLinesLinuxControllers(lines); + const nvidiaData = nvidiaDevices(); + // needs to be rewritten ... using no spread operators + result.controllers = result.controllers.map((controller) => { // match by busAddress + return mergeControllerNvidia(controller, nvidiaData.find((contr) => contr.pciBus.toLowerCase().endsWith(controller.busAddress.toLowerCase())) || {}); + }); + } + let cmd = 'clinfo --raw'; + exec(cmd, function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + result.controllers = parseLinesLinuxClinfo(result.controllers, lines); + } + let cmd = 'xdpyinfo 2>/dev/null | grep \'depth of root window\' | awk \'{ print $5 }\''; + exec(cmd, function (error, stdout) { + let depth = 0; + if (!error) { + let lines = stdout.toString().split('\n'); + depth = parseInt(lines[0]) || 0; + } + let cmd = 'xrandr --verbose 2>/dev/null'; + exec(cmd, function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + result.displays = parseLinesLinuxDisplays(lines, depth); + } + if (callback) { + callback(result); + } + resolve(result); + }); + }); + }); + }); + } + } + if (_freebsd || _openbsd || _netbsd) { + if (callback) { callback(null); } + resolve(null); + } + if (_sunos) { + if (callback) { callback(null); } + resolve(null); + } + if (_windows) { + + // https://blogs.technet.microsoft.com/heyscriptingguy/2013/10/03/use-powershell-to-discover-multi-monitor-information/ + // https://devblogs.microsoft.com/scripting/use-powershell-to-discover-multi-monitor-information/ + try { + const workload = []; + workload.push(util.powerShell('Get-WmiObject win32_VideoController | fl *')); + workload.push(util.powerShell('gp "HKLM:\\SYSTEM\\ControlSet001\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}\\*" -ErrorAction SilentlyContinue | where MatchingDeviceId $null -NE | select MatchingDeviceId,HardwareInformation.qwMemorySize | fl')); + workload.push(util.powerShell('Get-WmiObject win32_desktopmonitor | fl *')); + workload.push(util.powerShell('Get-CimInstance -Namespace root\\wmi -ClassName WmiMonitorBasicDisplayParams | fl')); + workload.push(util.powerShell('Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Screen]::AllScreens')); + workload.push(util.powerShell('Get-CimInstance -Namespace root\\wmi -ClassName WmiMonitorConnectionParams | fl')); + workload.push(util.powerShell('gwmi WmiMonitorID -Namespace root\\wmi | ForEach-Object {(($_.ManufacturerName -notmatch 0 | foreach {[char]$_}) -join "") + "|" + (($_.ProductCodeID -notmatch 0 | foreach {[char]$_}) -join "") + "|" + (($_.UserFriendlyName -notmatch 0 | foreach {[char]$_}) -join "") + "|" + (($_.SerialNumberID -notmatch 0 | foreach {[char]$_}) -join "") + "|" + $_.InstanceName}')); + + const nvidiaData = nvidiaDevices(); + + Promise.all( + workload + ).then(data => { + // controller + vram + let csections = data[0].replace(/\r/g, '').split(/\n\s*\n/); + let vsections = data[1].replace(/\r/g, '').split(/\n\s*\n/); + result.controllers = parseLinesWindowsControllers(csections, vsections); + result.controllers = result.controllers.map((controller) => { // match by subDeviceId + if (controller.vendor.toLowerCase() === 'nvidia') { + return mergeControllerNvidia(controller, nvidiaData.find(device => { + let windowsSubDeviceId = (controller.subDeviceId || '').toLowerCase(); + const nvidiaSubDeviceIdParts = device.subDeviceId.split('x'); + let nvidiaSubDeviceId = nvidiaSubDeviceIdParts.length > 1 ? nvidiaSubDeviceIdParts[1].toLowerCase() : nvidiaSubDeviceIdParts[0].toLowerCase(); + const lengthDifference = Math.abs(windowsSubDeviceId.length - nvidiaSubDeviceId.length); + if (windowsSubDeviceId.length > nvidiaSubDeviceId.length) { + for (let i = 0; i < lengthDifference; i++) { + nvidiaSubDeviceId = '0' + nvidiaSubDeviceId; + } + } else if (windowsSubDeviceId.length < nvidiaSubDeviceId.length) { + for (let i = 0; i < lengthDifference; i++) { + windowsSubDeviceId = '0' + windowsSubDeviceId; + } + } + return windowsSubDeviceId === nvidiaSubDeviceId; + }) || {}); + } else { + return controller; + } + }); + + // displays + let dsections = data[2].replace(/\r/g, '').split(/\n\s*\n/); + // result.displays = parseLinesWindowsDisplays(dsections); + if (dsections[0].trim() === '') { dsections.shift(); } + if (dsections.length && dsections[dsections.length - 1].trim() === '') { dsections.pop(); } + + // monitor (powershell) + let msections = data[3].replace(/\r/g, '').split('Active '); + msections.shift(); + + // forms.screens (powershell) + let ssections = data[4].replace(/\r/g, '').split('BitsPerPixel '); + ssections.shift(); + + // connection params (powershell) - video type + let tsections = data[5].replace(/\r/g, '').split(/\n\s*\n/); + tsections.shift(); + + // monitor ID (powershell) - model / vendor + const res = data[6].replace(/\r/g, '').split(/\n/); + let isections = []; + res.forEach(element => { + const parts = element.split('|'); + if (parts.length === 5) { + isections.push({ + vendor: parts[0], + code: parts[1], + model: parts[2], + serial: parts[3], + instanceId: parts[4] + }); + } + }); + + result.displays = parseLinesWindowsDisplaysPowershell(ssections, msections, dsections, tsections, isections); + + if (result.displays.length === 1) { + if (_resolutionX) { + result.displays[0].resolutionX = _resolutionX; + if (!result.displays[0].currentResX) { + result.displays[0].currentResX = _resolutionX; + } + } + if (_resolutionY) { + result.displays[0].resolutionY = _resolutionY; + if (result.displays[0].currentResY === 0) { + result.displays[0].currentResY = _resolutionY; + } + } + if (_pixelDepth) { + result.displays[0].pixelDepth = _pixelDepth; + } + if (_refreshRate && !result.displays[0].currentRefreshRate) { + result.displays[0].currentRefreshRate = _refreshRate; + } + } + + if (callback) { + callback(result); + } + resolve(result); + }) + .catch(() => { + if (callback) { + callback(result); + } + resolve(result); + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + }); + }); + + function parseLinesWindowsControllers(sections, vections) { + const memorySizes = {}; + for (const i in vections) { + if ({}.hasOwnProperty.call(vections, i)) { + if (vections[i].trim() !== '') { + const lines = vections[i].trim().split('\n'); + const matchingDeviceId = util.getValue(lines, 'MatchingDeviceId').match(/PCI\\(VEN_[0-9A-F]{4})&(DEV_[0-9A-F]{4})(?:&(SUBSYS_[0-9A-F]{8}))?(?:&(REV_[0-9A-F]{2}))?/i); + if (matchingDeviceId) { + const quadWordmemorySize = parseInt(util.getValue(lines, 'HardwareInformation.qwMemorySize')); + if (!isNaN(quadWordmemorySize)) { + let deviceId = matchingDeviceId[1].toUpperCase() + '&' + matchingDeviceId[2].toUpperCase(); + if (matchingDeviceId[3]) { + deviceId += '&' + matchingDeviceId[3].toUpperCase(); + } + if (matchingDeviceId[4]) { + deviceId += '&' + matchingDeviceId[4].toUpperCase(); + } + memorySizes[deviceId] = quadWordmemorySize; + } + } + } + } + } + + let controllers = []; + for (let i in sections) { + if ({}.hasOwnProperty.call(sections, i)) { + if (sections[i].trim() !== '') { + let lines = sections[i].trim().split('\n'); + let pnpDeviceId = util.getValue(lines, 'PNPDeviceID', ':').match(/PCI\\(VEN_[0-9A-F]{4})&(DEV_[0-9A-F]{4})(?:&(SUBSYS_[0-9A-F]{8}))?(?:&(REV_[0-9A-F]{2}))?/i); + let subDeviceId = null; + let memorySize = null; + if (pnpDeviceId) { + subDeviceId = pnpDeviceId[3] || ''; + if (subDeviceId) { + subDeviceId = subDeviceId.split('_')[1]; + } + + // Match PCI device identifier (there's an order of increasing generality): + // https://docs.microsoft.com/en-us/windows-hardware/drivers/install/identifiers-for-pci-devices + + // PCI\VEN_v(4)&DEV_d(4)&SUBSYS_s(4)n(4)&REV_r(2) + if (memorySize == null && pnpDeviceId[3] && pnpDeviceId[4]) { + const deviceId = pnpDeviceId[1].toUpperCase() + '&' + pnpDeviceId[2].toUpperCase() + '&' + pnpDeviceId[3].toUpperCase() + '&' + pnpDeviceId[4].toUpperCase(); + if ({}.hasOwnProperty.call(memorySizes, deviceId)) { + memorySize = memorySizes[deviceId]; + } + } + + // PCI\VEN_v(4)&DEV_d(4)&SUBSYS_s(4)n(4) + if (memorySize == null && pnpDeviceId[3]) { + const deviceId = pnpDeviceId[1].toUpperCase() + '&' + pnpDeviceId[2].toUpperCase() + '&' + pnpDeviceId[3].toUpperCase(); + if ({}.hasOwnProperty.call(memorySizes, deviceId)) { + memorySize = memorySizes[deviceId]; + } + } + + // PCI\VEN_v(4)&DEV_d(4)&REV_r(2) + if (memorySize == null && pnpDeviceId[4]) { + const deviceId = pnpDeviceId[1].toUpperCase() + '&' + pnpDeviceId[2].toUpperCase() + '&' + pnpDeviceId[4].toUpperCase(); + if ({}.hasOwnProperty.call(memorySizes, deviceId)) { + memorySize = memorySizes[deviceId]; + } + } + + // PCI\VEN_v(4)&DEV_d(4) + if (memorySize == null) { + const deviceId = pnpDeviceId[1].toUpperCase() + '&' + pnpDeviceId[2].toUpperCase(); + if ({}.hasOwnProperty.call(memorySizes, deviceId)) { + memorySize = memorySizes[deviceId]; + } + } + } + + controllers.push({ + vendor: util.getValue(lines, 'AdapterCompatibility', ':'), + model: util.getValue(lines, 'name', ':'), + bus: util.getValue(lines, 'PNPDeviceID', ':').startsWith('PCI') ? 'PCI' : '', + vram: (memorySize == null ? util.toInt(util.getValue(lines, 'AdapterRAM', ':')) : memorySize) / 1024 / 1024, + vramDynamic: (util.getValue(lines, 'VideoMemoryType', ':') === '2'), + subDeviceId + }); + _resolutionX = util.toInt(util.getValue(lines, 'CurrentHorizontalResolution', ':')) || _resolutionX; + _resolutionY = util.toInt(util.getValue(lines, 'CurrentVerticalResolution', ':')) || _resolutionY; + _refreshRate = util.toInt(util.getValue(lines, 'CurrentRefreshRate', ':')) || _refreshRate; + _pixelDepth = util.toInt(util.getValue(lines, 'CurrentBitsPerPixel', ':')) || _pixelDepth; + } + } + } + return controllers; + } + + function parseLinesWindowsDisplaysPowershell(ssections, msections, dsections, tsections, isections) { + let displays = []; + let vendor = ''; + let model = ''; + let deviceID = ''; + let resolutionX = 0; + let resolutionY = 0; + if (dsections && dsections.length) { + let linesDisplay = dsections[0].split('\n'); + vendor = util.getValue(linesDisplay, 'MonitorManufacturer', ':'); + model = util.getValue(linesDisplay, 'Name', ':'); + deviceID = util.getValue(linesDisplay, 'PNPDeviceID', ':').replace(/&/g, '&').toLowerCase(); + resolutionX = util.toInt(util.getValue(linesDisplay, 'ScreenWidth', ':')); + resolutionY = util.toInt(util.getValue(linesDisplay, 'ScreenHeight', ':')); + } + for (let i = 0; i < ssections.length; i++) { + if (ssections[i].trim() !== '') { + ssections[i] = 'BitsPerPixel ' + ssections[i]; + msections[i] = 'Active ' + msections[i]; + // tsections can be empty OR undefined on earlier versions of powershell (<=2.0) + // Tag connection type as UNKNOWN by default if this information is missing + if (tsections.length === 0 || tsections[i] === undefined) { + tsections[i] = 'Unknown'; + } + let linesScreen = ssections[i].split('\n'); + let linesMonitor = msections[i].split('\n'); + + let linesConnection = tsections[i].split('\n'); + const bitsPerPixel = util.getValue(linesScreen, 'BitsPerPixel'); + const bounds = util.getValue(linesScreen, 'Bounds').replace('{', '').replace('}', '').replace(/=/g, ':').split(','); + const primary = util.getValue(linesScreen, 'Primary'); + const sizeX = util.getValue(linesMonitor, 'MaxHorizontalImageSize'); + const sizeY = util.getValue(linesMonitor, 'MaxVerticalImageSize'); + const instanceName = util.getValue(linesMonitor, 'InstanceName').toLowerCase(); + const videoOutputTechnology = util.getValue(linesConnection, 'VideoOutputTechnology'); + const deviceName = util.getValue(linesScreen, 'DeviceName'); + let displayVendor = ''; + let displayModel = ''; + isections.forEach(element => { + if (element.instanceId.toLowerCase().startsWith(instanceName) && vendor.startsWith('(') && model.startsWith('PnP')) { + displayVendor = element.vendor; + displayModel = element.model; + } + }); + displays.push({ + vendor: instanceName.startsWith(deviceID) && displayVendor === '' ? vendor : displayVendor, + model: instanceName.startsWith(deviceID) && displayModel === '' ? model : displayModel, + deviceName, + main: primary.toLowerCase() === 'true', + builtin: videoOutputTechnology === '2147483648', + connection: videoOutputTechnology && videoTypes[videoOutputTechnology] ? videoTypes[videoOutputTechnology] : '', + resolutionX: util.toInt(util.getValue(bounds, 'Width', ':')), + resolutionY: util.toInt(util.getValue(bounds, 'Height', ':')), + sizeX: sizeX ? parseInt(sizeX, 10) : null, + sizeY: sizeY ? parseInt(sizeY, 10) : null, + pixelDepth: bitsPerPixel, + currentResX: util.toInt(util.getValue(bounds, 'Width', ':')), + currentResY: util.toInt(util.getValue(bounds, 'Height', ':')), + positionX: util.toInt(util.getValue(bounds, 'X', ':')), + positionY: util.toInt(util.getValue(bounds, 'Y', ':')), + }); + } + } + if (ssections.length === 0) { + displays.push({ + vendor, + model, + main: true, + sizeX: null, + sizeY: null, + resolutionX, + resolutionY, + pixelDepth: null, + currentResX: resolutionX, + currentResY: resolutionY, + positionX: 0, + positionY: 0 + }); + } + return displays; + } +} + +exports.graphics = graphics; diff --git a/node_modules/systeminformation/lib/index.d.ts b/node_modules/systeminformation/lib/index.d.ts new file mode 100644 index 0000000..f0efaeb --- /dev/null +++ b/node_modules/systeminformation/lib/index.d.ts @@ -0,0 +1,990 @@ +// Type definitions for systeminformation +// Project: https://github.com/sebhildebrandt/systeminformation +// Definitions by: sebhildebrandt <https://github.com/sebhildebrandt> + +export namespace Systeminformation { + + // 1. General + + interface TimeData { + current: string; + uptime: string; + timezone: string; + timezoneName: string; + } + + // 2. System (HW) + + interface RaspberryRevisionData { + manufacturer: string; + processor: string; + type: string; + revision: string; + } + interface SystemData { + manufacturer: string; + model: string; + version: string; + serial: string; + uuid: string; + sku: string; + virtual: boolean; + virtualHost?: string; + raspberry?: RaspberryRevisionData; + } + + interface BiosData { + vendor: string; + version: string; + releaseDate: string; + revision: string; + serial?: string; + language?: string; + features?: string[]; + } + + interface BaseboardData { + manufacturer: string; + model: string; + version: string; + serial: string; + assetTag: string; + memMax: number | null; + memSlots: number | null; + } + + interface ChassisData { + manufacturer: string; + model: string; + type: string; + version: string; + serial: string; + assetTag: string; + sku: string; + } + + // 3. CPU, Memory, Disks, Battery, Graphics + + interface CpuData { + manufacturer: string; + brand: string; + vendor: string; + family: string; + model: string; + stepping: string; + revision: string; + voltage: string; + speed: number; + speedMin: number; + speedMax: number; + governor: string; + cores: number; + physicalCores: number; + efficiencyCores?: number; + performanceCores?: number; + processors: number; + socket: string; + flags: string; + virtualization: boolean; + cache: CpuCacheData; + } + + interface CpuCacheData { + l1d: number; + l1i: number; + l2: number; + l3: number; + } + + interface CpuCurrentSpeedData { + min: number; + max: number; + avg: number; + cores: number[]; + } + + interface CpuTemperatureData { + main: number; + cores: number[]; + max: number; + socket?: number[]; + chipset?: number; + } + + interface MemData { + total: number; + free: number; + used: number; + active: number; + available: number; + buffcache: number; + buffers: number; + cached: number; + slab: number; + swaptotal: number; + swapused: number; + swapfree: number; + } + + interface MemLayoutData { + size: number; + bank: string; + type: string; + ecc?: boolean | null; + clockSpeed: number | null; + formFactor: string; + manufacturer?: string; + partNum: string; + serialNum: string; + voltageConfigured: number | null; + voltageMin: number | null; + voltageMax: number | null; + } + + interface SmartData { + json_format_version: number[]; + smartctl: { + version: number[]; + platform_info: string; + build_info: string; + argv: string[]; + exit_status: number; + }; + device: { + name: string; + info_name: string; + type: string; + protocol: string; + }; + model_family?: string, + model_name?: string, + serial_number?: string, + firmware_version?: string, + smart_status: { + passed: boolean; + }; + trim?: { + supported: boolean; + }, + ata_smart_attributes?: { + revision: number; + table: { + id: number; + name: string; + value: number; + worst: number; + thresh: number; + when_failed: string; + flags: { + value: number; + string: string; + prefailure: boolean; + updated_online: boolean; + performance: boolean; + error_rate: boolean; + event_count: boolean; + auto_keep: boolean; + }; + raw: { value: number; string: string; }; + }[]; + }; + ata_smart_error_log?: { + summary: { + revision: number; + count: number; + }; + }; + ata_smart_self_test_log?: { + standard: { + revision: number; + table: { + type: { + value: number; + string: string; + }, + status: { + value: number; + string: string; + passed: boolean; + }, + lifetime_hours: number; + }[]; + count: number; + error_count_total: number; + error_count_outdated: number; + }; + }; + nvme_pci_vendor?: { + id: number, + subsystem_id: number; + }, + nvme_smart_health_information_log?: { + critical_warning?: number, + temperature?: number, + available_spare?: number, + available_spare_threshold?: number, + percentage_used?: number, + data_units_read?: number, + data_units_written?: number, + host_reads?: number, + host_writes?: number, + controller_busy_time?: number, + power_cycles?: number, + power_on_hours?: number, + unsafe_shutdowns?: number, + media_errors?: number, + num_err_log_entries?: number, + warning_temp_time?: number, + critical_comp_time?: number, + temperature_sensors?: number[]; + }, + user_capacity?: { + blocks: number, + bytes: number; + }, + logical_block_size?: number, + temperature: { + current: number; + }; + power_cycle_count: number; + power_on_time: { + hours: number; + }; + } + + interface DiskLayoutData { + device: string; + type: string; + name: string; + vendor: string; + size: number; + bytesPerSector: number; + totalCylinders: number; + totalHeads: number; + totalSectors: number; + totalTracks: number; + tracksPerCylinder: number; + sectorsPerTrack: number; + firmwareRevision: string; + serialNum: string; + interfaceType: string; + smartStatus: string; + temperature: null | number; + smartData?: SmartData; + } + + interface BatteryData { + hasBattery: boolean; + cycleCount: number; + isCharging: boolean; + voltage: number; + designedCapacity: number; + maxCapacity: number; + currentCapacity: number; + capacityUnit: string; + percent: number; + timeRemaining: number, + acConnected: boolean; + type: string; + model: string; + manufacturer: string; + serial: string; + additionalBatteries?: BatteryData[]; + } + + interface GraphicsData { + controllers: GraphicsControllerData[]; + displays: GraphicsDisplayData[]; + } + + interface GraphicsControllerData { + vendor: string; + vendorId?: string; + model: string; + deviceId?: string; + bus: string; + busAddress?: string; + vram: number | null; + vramDynamic: boolean; + external?: boolean; + cores?: number; + metalVersion?: string; + subDeviceId?: string; + driverVersion?: string; + name?: string; + pciBus?: string; + pciID?: string; + fanSpeed?: number; + memoryTotal?: number; + memoryUsed?: number; + memoryFree?: number; + utilizationGpu?: number; + utilizationMemory?: number; + temperatureGpu?: number; + temperatureMemory?: number; + powerDraw?: number; + powerLimit?: number; + clockCore?: number; + clockMemory?: number; + } + + interface GraphicsDisplayData { + vendor: string; + vendorId: string | null; + model: string; + productionYear: number | null; + serial: string | null; + deviceName: string | null; + displayId: string | null; + main: boolean; + builtin: boolean; + connection: string | null; + sizeX: number | null; + sizeY: number | null; + pixelDepth: number | null; + resolutionX: number | null; + resolutionY: number | null; + currentResX: number | null; + currentResY: number | null; + positionX: number; + positionY: number; + currentRefreshRate: number | null; + } + + // 4. Operating System + + interface OsData { + platform: string; + distro: string; + release: string; + codename: string; + kernel: string; + arch: string; + hostname: string; + fqdn: string; + codepage: string; + logofile: string; + serial: string; + build: string; + servicepack: string; + uefi: boolean; + hypervizor?: boolean; + remoteSession?: boolean; + hypervisor?: boolean; + } + + interface UuidData { + os: string; + hardware: string; + macs: string[]; + } + + interface VersionData { + kernel?: string; + openssl?: string; + systemOpenssl?: string; + systemOpensslLib?: string; + node?: string; + v8?: string; + npm?: string; + yarn?: string; + pm2?: string; + gulp?: string; + grunt?: string; + git?: string; + tsc?: string; + mysql?: string; + redis?: string; + mongodb?: string; + nginx?: string; + php?: string; + docker?: string; + postfix?: string; + postgresql?: string; + perl?: string; + python?: string; + python3?: string; + pip?: string; + pip3?: string; + java?: string; + gcc?: string; + virtualbox?: string; + dotnet?: string; + } + + interface UserData { + user: string; + tty: string; + date: string; + time: string; + ip: string; + command: string; + } + + // 5. File System + + interface FsSizeData { + fs: string; + type: string; + size: number; + used: number; + available: number; + use: number; + mount: string; + } + + interface FsOpenFilesData { + max: number; + allocated: number; + available: number; + } + + interface BlockDevicesData { + name: string; + identifier: string; + type: string; + fsType: string; + mount: string; + size: number; + physical: string; + uuid: string; + label: string; + model: string; + serial: string; + removable: boolean; + protocol: string; + } + + interface FsStatsData { + rx: number; + wx: number; + tx: number; + rx_sec: number | null; + wx_sec: number | null; + tx_sec: number | null; + ms: number; + } + + interface DisksIoData { + rIO: number; + wIO: number; + tIO: number; + rIO_sec: number | null; + wIO_sec: number | null; + tIO_sec: number | null; + rWaitTime: number; + wWaitTime: number; + tWaitTime: number; + rWaitPercent: number | null; + wWaitPercent: number | null; + tWaitPercent: number | null; + ms: number; + } + + // 6. Network related functions + + interface NetworkInterfacesData { + iface: string; + ifaceName: string; + ip4: string; + ip4subnet: string; + ip6: string; + ip6subnet: string; + mac: string; + internal: boolean; + virtual: boolean; + operstate: string; + type: string; + duplex: string; + mtu: number; + speed: number; + dhcp: boolean; + dnsSuffix: string; + ieee8021xAuth: string; + ieee8021xState: string; + carrierChanges: number; + } + + interface NetworkStatsData { + iface: string; + operstate: string; + rx_bytes: number; + rx_dropped: number; + rx_errors: number; + tx_bytes: number; + tx_dropped: number; + tx_errors: number; + rx_sec: number; + tx_sec: number; + ms: number; + } + + interface NetworkConnectionsData { + protocol: string; + localAddress: string; + localPort: string; + peerAddress: string; + peerPort: string; + state: string; + pid: number; + process: string; + } + + interface InetChecksiteData { + url: string; + ok: boolean; + status: number; + ms: number; + } + + interface WifiNetworkData { + ssid: string; + bssid: string; + mode: string; + channel: number; + frequency: number; + signalLevel: number; + quality: number; + security: string[]; + wpaFlags: string[]; + rsnFlags: string[]; + } + + interface WifiInterfaceData { + id: string; + iface: string; + model: string; + vendor: string; + mac: string; + } + + interface WifiConnectionData { + id: string; + iface: string; + model: string; + ssid: string; + bssid: string; + channel: number; + type: string; + security: string; + frequency: number; + signalLevel: number; + txRate: number; + } + + // 7. Current Load, Processes & Services + + interface CurrentLoadData { + avgLoad: number; + currentLoad: number; + currentLoadUser: number; + currentLoadSystem: number; + currentLoadNice: number; + currentLoadIdle: number; + currentLoadIrq: number; + rawCurrentLoad: number; + rawCurrentLoadUser: number; + rawCurrentLoadSystem: number; + rawCurrentLoadNice: number; + rawCurrentLoadIdle: number; + rawCurrentLoadIrq: number; + cpus: CurrentLoadCpuData[]; + } + + interface CurrentLoadCpuData { + load: number; + loadUser: number; + loadSystem: number; + loadNice: number; + loadIdle: number; + loadIrq: number; + rawLoad: number; + rawLoadUser: number; + rawLoadSystem: number; + rawLoadNice: number; + rawLoadIdle: number; + rawLoadIrq: number; + } + + interface ProcessesData { + all: number; + running: number; + blocked: number; + sleeping: number; + unknown: number; + list: ProcessesProcessData[]; + } + + interface ProcessesProcessData { + pid: number; + parentPid: number; + name: string, + cpu: number; + cpuu: number; + cpus: number; + mem: number; + priority: number; + memVsz: number; + memRss: number; + nice: number; + started: string, + state: string; + tty: string; + user: string; + command: string; + params: string; + path: string; + } + + interface ProcessesProcessLoadData { + proc: string; + pid: number; + pids: number[]; + cpu: number; + mem: number; + } + + interface ServicesData { + name: string; + running: boolean; + startmode: string; + pids: number[]; + cpu: number; + mem: number; + } + + // 8. Docker + + interface DockerInfoData { + id: string; + containers: number; + containersRunning: number; + containersPaused: number; + containersStopped: number; + images: number; + driver: string; + memoryLimit: boolean; + swapLimit: boolean; + kernelMemory: boolean; + cpuCfsPeriod: boolean; + cpuCfsQuota: boolean; + cpuShares: boolean; + cpuSet: boolean; + ipv4Forwarding: boolean; + bridgeNfIptables: boolean; + bridgeNfIp6tables: boolean; + debug: boolean; + nfd: number; + oomKillDisable: boolean; + ngoroutines: number; + systemTime: string; + loggingDriver: string; + cgroupDriver: string; + nEventsListener: number; + kernelVersion: string; + operatingSystem: string; + osType: string; + architecture: string; + ncpu: number; + memTotal: number; + dockerRootDir: string; + httpProxy: string; + httpsProxy: string; + noProxy: string; + name: string; + labels: string[]; + experimentalBuild: boolean; + serverVersion: string; + clusterStore: string; + clusterAdvertise: string; + defaultRuntime: string; + liveRestoreEnabled: boolean; + isolation: string; + initBinary: string; + productLicense: string; + } + + interface DockerImageData { + id: string; + container: string; + comment: string; + os: string; + architecture: string; + parent: string; + dockerVersion: string; + size: number; + sharedSize: number; + virtualSize: number; + author: string; + created: number; + containerConfig: any; + graphDriver: any; + repoDigests: any; + repoTags: any; + config: any; + rootFS: any; + } + + interface DockerContainerData { + id: string; + name: string; + image: string; + imageID: string; + command: string; + created: number; + started: number; + finished: number; + createdAt: string; + startedAt: string; + finishedAt: string; + state: string; + restartCount: number; + platform: string; + driver: string; + ports: number[]; + mounts: DockerContainerMountData[]; + } + + interface DockerContainerMountData { + Type: string; + Source: string; + Destination: string; + Mode: string; + RW: boolean; + Propagation: string; + } + + interface DockerContainerStatsData { + id: string; + memUsage: number; + memLimit: number; + memPercent: number; + cpuPercent: number; + pids: number; + netIO: { + rx: number; + wx: number; + }; + blockIO: { + r: number; + w: number; + }; + restartCount: number; + cpuStats: any; + precpuStats: any; + memoryStats: any, + networks: any; + } + + interface DockerContainerProcessData { + pidHost: string; + ppid: string; + pgid: string; + user: string; + ruser: string; + group: string; + rgroup: string; + stat: string; + time: string; + elapsed: string; + nice: string; + rss: string; + vsz: string; + command: string; + } + + interface DockerVolumeData { + name: string; + driver: string; + labels: any; + mountpoint: string; + options: any; + scope: string; + created: number; + } + + // 9. Virtual Box + + interface VboxInfoData { + id: string; + name: string; + running: boolean; + started: string; + runningSince: number; + stopped: string; + stoppedSince: number; + guestOS: string; + hardwareUUID: string; + memory: number; + vram: number; + cpus: number; + cpuExepCap: string; + cpuProfile: string; + chipset: string; + firmware: string; + pageFusion: boolean; + configFile: string; + snapshotFolder: string; + logFolder: string; + hpet: boolean; + pae: boolean; + longMode: boolean; + tripleFaultReset: boolean; + apic: boolean; + x2Apic: boolean; + acpi: boolean; + ioApic: boolean; + biosApicMode: string; + bootMenuMode: string; + bootDevice1: string; + bootDevice2: string; + bootDevice3: string; + bootDevice4: string; + timeOffset: string; + rtc: string; + } + + interface PrinterData { + id: number; + name: string; + model: string; + uri: string; + uuid: string; + local: boolean; + status: string; + default: boolean; + shared: boolean; + } + + interface UsbData { + id: number | string; + bus: number; + deviceId: number; + name: string; + type: string; + removable: boolean; + vendor: string; + manufacturer: string; + maxPower: string; + serialNumber: string; + } + + interface AudioData { + id: number | string; + name: string; + manufacturer: string; + default: boolean; + revision: string; + driver: string; + channel: string; + in: boolean; + out: boolean; + type: string; + status: string; + } + + interface BluetoothDeviceData { + device: string; + name: string; + macDevice: string; + macHost: string; + batteryPercent: number; + manufacturer: string; + type: string; + connected: boolean; + } + + // 10. "Get All at once" - functions + + interface StaticData { + version: string; + system: SystemData; + bios: BiosData; + baseboard: BaseboardData; + chassis: ChassisData; + os: OsData; + uuid: UuidData; + versions: VersionData; + cpu: CpuData; + graphics: GraphicsData; + net: NetworkInterfacesData[]; + memLayout: MemLayoutData[]; + diskLayout: DiskLayoutData[]; + } +} + +export function version(): string; +export function system(cb?: (data: Systeminformation.SystemData) => any): Promise<Systeminformation.SystemData>; +export function bios(cb?: (data: Systeminformation.BiosData) => any): Promise<Systeminformation.BiosData>; +export function baseboard(cb?: (data: Systeminformation.BaseboardData) => any): Promise<Systeminformation.BaseboardData>; +export function chassis(cb?: (data: Systeminformation.ChassisData) => any): Promise<Systeminformation.ChassisData>; + +export function time(): Systeminformation.TimeData; +export function osInfo(cb?: (data: Systeminformation.OsData) => any): Promise<Systeminformation.OsData>; +export function versions(apps?: string, cb?: (data: Systeminformation.VersionData) => any): Promise<Systeminformation.VersionData>; +export function shell(cb?: (data: string) => any): Promise<string>; +export function uuid(cb?: (data: Systeminformation.UuidData) => any): Promise<Systeminformation.UuidData>; + +export function cpu(cb?: (data: Systeminformation.CpuData) => any): Promise<Systeminformation.CpuData>; +export function cpuFlags(cb?: (data: string) => any): Promise<string>; +export function cpuCache(cb?: (data: Systeminformation.CpuCacheData) => any): Promise<Systeminformation.CpuCacheData>; +export function cpuCurrentSpeed(cb?: (data: Systeminformation.CpuCurrentSpeedData) => any): Promise<Systeminformation.CpuCurrentSpeedData>; +export function cpuTemperature(cb?: (data: Systeminformation.CpuTemperatureData) => any): Promise<Systeminformation.CpuTemperatureData>; +export function currentLoad(cb?: (data: Systeminformation.CurrentLoadData) => any): Promise<Systeminformation.CurrentLoadData>; +export function fullLoad(cb?: (data: number) => any): Promise<number>; + +export function mem(cb?: (data: Systeminformation.MemData) => any): Promise<Systeminformation.MemData>; +export function memLayout(cb?: (data: Systeminformation.MemLayoutData[]) => any): Promise<Systeminformation.MemLayoutData[]>; + +export function battery(cb?: (data: Systeminformation.BatteryData) => any): Promise<Systeminformation.BatteryData>; +export function graphics(cb?: (data: Systeminformation.GraphicsData) => any): Promise<Systeminformation.GraphicsData>; + +export function fsSize(cb?: (data: Systeminformation.FsSizeData[]) => any): Promise<Systeminformation.FsSizeData[]>; +export function fsOpenFiles(cb?: (data: Systeminformation.FsOpenFilesData[]) => any): Promise<Systeminformation.FsOpenFilesData[]>; +export function blockDevices(cb?: (data: Systeminformation.BlockDevicesData[]) => any): Promise<Systeminformation.BlockDevicesData[]>; +export function fsStats(cb?: (data: Systeminformation.FsStatsData) => any): Promise<Systeminformation.FsStatsData>; +export function disksIO(cb?: (data: Systeminformation.DisksIoData) => any): Promise<Systeminformation.DisksIoData>; +export function diskLayout(cb?: (data: Systeminformation.DiskLayoutData[]) => any): Promise<Systeminformation.DiskLayoutData[]>; + +export function networkInterfaceDefault(cb?: (data: string) => any): Promise<string>; +export function networkGatewayDefault(cb?: (data: string) => any): Promise<string>; +export function networkInterfaces(cb?: (data: Systeminformation.NetworkInterfacesData[]) => any): Promise<Systeminformation.NetworkInterfacesData[]>; + +export function networkStats(ifaces?: string, cb?: (data: Systeminformation.NetworkStatsData[]) => any): Promise<Systeminformation.NetworkStatsData[]>; +export function networkConnections(cb?: (data: Systeminformation.NetworkConnectionsData[]) => any): Promise<Systeminformation.NetworkConnectionsData[]>; +export function inetChecksite(url: string, cb?: (data: Systeminformation.InetChecksiteData) => any): Promise<Systeminformation.InetChecksiteData>; +export function inetLatency(host?: string, cb?: (data: number) => any): Promise<number>; + +export function wifiNetworks(cb?: (data: Systeminformation.WifiNetworkData[]) => any): Promise<Systeminformation.WifiNetworkData[]>; +export function wifiInterfaces(cb?: (data: Systeminformation.WifiInterfaceData[]) => any): Promise<Systeminformation.WifiInterfaceData[]>; +export function wifiConnections(cb?: (data: Systeminformation.WifiConnectionData[]) => any): Promise<Systeminformation.WifiConnectionData[]>; + +export function users(cb?: (data: Systeminformation.UserData[]) => any): Promise<Systeminformation.UserData[]>; + +export function processes(cb?: (data: Systeminformation.ProcessesData) => any): Promise<Systeminformation.ProcessesData>; +export function processLoad(processNames: string, cb?: (data: Systeminformation.ProcessesProcessLoadData[]) => any): Promise<Systeminformation.ProcessesProcessLoadData[]>; +export function services(serviceName: string, cb?: (data: Systeminformation.ServicesData[]) => any): Promise<Systeminformation.ServicesData[]>; + +export function dockerInfo(cb?: (data: Systeminformation.DockerInfoData) => any): Promise<Systeminformation.DockerInfoData>; +export function dockerImages(all?: boolean, cb?: (data: Systeminformation.DockerImageData[]) => any): Promise<Systeminformation.DockerImageData[]>; +export function dockerContainers(all?: boolean, cb?: (data: Systeminformation.DockerContainerData[]) => any): Promise<Systeminformation.DockerContainerData[]>; +export function dockerContainerStats(id?: string, cb?: (data: Systeminformation.DockerContainerStatsData[]) => any): Promise<Systeminformation.DockerContainerStatsData[]>; +export function dockerContainerProcesses(id?: string, cb?: (data: any) => any): Promise<Systeminformation.DockerContainerProcessData[]>; +export function dockerVolumes(cb?: (data: Systeminformation.DockerVolumeData[]) => any): Promise<Systeminformation.DockerVolumeData[]>; +export function dockerAll(cb?: (data: any) => any): Promise<any>; + +export function vboxInfo(cb?: (data: Systeminformation.VboxInfoData[]) => any): Promise<Systeminformation.VboxInfoData[]>; + +export function printer(cb?: (data: Systeminformation.PrinterData[]) => any): Promise<Systeminformation.PrinterData[]>; + +export function usb(cb?: (data: Systeminformation.UsbData[]) => any): Promise<Systeminformation.UsbData[]>; + +export function audio(cb?: (data: Systeminformation.AudioData[]) => any): Promise<Systeminformation.AudioData[]>; + +export function bluetoothDevices(cb?: (data: Systeminformation.BluetoothDeviceData[]) => any): Promise<Systeminformation.BluetoothDeviceData[]>; + +export function getStaticData(cb?: (data: Systeminformation.StaticData) => any): Promise<Systeminformation.StaticData>; +export function getDynamicData(srv?: string, iface?: string, cb?: (data: any) => any): Promise<any>; +export function getAllData(srv?: string, iface?: string, cb?: (data: any) => any): Promise<any>; +export function get(valuesObject: any, cb?: (data: any) => any): Promise<any>; +export function observe(valuesObject: any, interval: number, cb?: (data: any) => any): number; diff --git a/node_modules/systeminformation/lib/index.js b/node_modules/systeminformation/lib/index.js new file mode 100644 index 0000000..5cad646 --- /dev/null +++ b/node_modules/systeminformation/lib/index.js @@ -0,0 +1,513 @@ +'use strict'; +// @ts-check +// ================================================================================== +// index.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// Contributors: Guillaume Legrain (https://github.com/glegrain) +// Riccardo Novaglia (https://github.com/richy24) +// Quentin Busuttil (https://github.com/Buzut) +// Lapsio (https://github.com/lapsio) +// csy (https://github.com/csy1983) +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== + +// ---------------------------------------------------------------------------------- +// Dependencies +// ---------------------------------------------------------------------------------- + +const lib_version = require('../package.json').version; +const util = require('./util'); +const system = require('./system'); +const osInfo = require('./osinfo'); +const cpu = require('./cpu'); +const memory = require('./memory'); +const battery = require('./battery'); +const graphics = require('./graphics'); +const filesystem = require('./filesystem'); +const network = require('./network'); +const wifi = require('./wifi'); +const processes = require('./processes'); +const users = require('./users'); +const internet = require('./internet'); +const docker = require('./docker'); +const vbox = require('./virtualbox'); +const printer = require('./printer'); +const usb = require('./usb'); +const audio = require('./audio'); +const bluetooth = require('./bluetooth'); + +let _platform = process.platform; +const _windows = (_platform === 'win32'); +const _freebsd = (_platform === 'freebsd'); +const _openbsd = (_platform === 'openbsd'); +const _netbsd = (_platform === 'netbsd'); +const _sunos = (_platform === 'sunos'); + +// ---------------------------------------------------------------------------------- +// init +// ---------------------------------------------------------------------------------- + +if (_windows) { + util.getCodepage(); +} + +// ---------------------------------------------------------------------------------- +// General +// ---------------------------------------------------------------------------------- + +function version() { + return lib_version; +} + +// ---------------------------------------------------------------------------------- +// Get static and dynamic data (all) +// ---------------------------------------------------------------------------------- + +// -------------------------- +// get static data - they should not change until restarted + +function getStaticData(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + + let data = {}; + + data.version = version(); + + Promise.all([ + system.system(), + system.bios(), + system.baseboard(), + system.chassis(), + osInfo.osInfo(), + osInfo.uuid(), + osInfo.versions(), + cpu.cpu(), + cpu.cpuFlags(), + graphics.graphics(), + network.networkInterfaces(), + memory.memLayout(), + filesystem.diskLayout() + ]).then(res => { + data.system = res[0]; + data.bios = res[1]; + data.baseboard = res[2]; + data.chassis = res[3]; + data.os = res[4]; + data.uuid = res[5]; + data.versions = res[6]; + data.cpu = res[7]; + data.cpu.flags = res[8]; + data.graphics = res[9]; + data.net = res[10]; + data.memLayout = res[11]; + data.diskLayout = res[12]; + if (callback) { callback(data); } + resolve(data); + }); + }); + }); +} + + +// -------------------------- +// get all dynamic data - e.g. for monitoring agents +// may take some seconds to get all data +// -------------------------- +// 2 additional parameters needed +// - srv: comma separated list of services to monitor e.g. "mysql, apache, postgresql" +// - iface: define network interface for which you like to monitor network speed e.g. "eth0" + +function getDynamicData(srv, iface, callback) { + + if (util.isFunction(iface)) { + callback = iface; + iface = ''; + } + if (util.isFunction(srv)) { + callback = srv; + srv = ''; + } + + return new Promise((resolve) => { + process.nextTick(() => { + + iface = iface || network.getDefaultNetworkInterface(); + srv = srv || ''; + + // use closure to track ƒ completion + let functionProcessed = (function () { + let totalFunctions = 15; + if (_windows) { totalFunctions = 13; } + if (_freebsd || _openbsd || _netbsd) { totalFunctions = 11; } + if (_sunos) { totalFunctions = 6; } + + return function () { + if (--totalFunctions === 0) { + if (callback) { + callback(data); + } + resolve(data); + } + }; + })(); + + // var totalFunctions = 14; + // function functionProcessed() { + // if (--totalFunctions === 0) { + // if (callback) { callback(data) } + // resolve(data); + // } + // } + + let data = {}; + + // get time + data.time = osInfo.time(); + + /** + * @namespace + * @property {Object} versions + * @property {string} versions.node + * @property {string} versions.v8 + */ + data.node = process.versions.node; + data.v8 = process.versions.v8; + + cpu.cpuCurrentSpeed().then(res => { + data.cpuCurrentSpeed = res; + functionProcessed(); + }); + + users.users().then(res => { + data.users = res; + functionProcessed(); + }); + + processes.processes().then(res => { + data.processes = res; + functionProcessed(); + }); + + cpu.currentLoad().then(res => { + data.currentLoad = res; + functionProcessed(); + }); + + if (!_sunos) { + cpu.cpuTemperature().then(res => { + data.temp = res; + functionProcessed(); + }); + } + + if (!_openbsd && !_freebsd && !_netbsd && !_sunos) { + network.networkStats(iface).then(res => { + data.networkStats = res; + functionProcessed(); + }); + } + + if (!_sunos) { + network.networkConnections().then(res => { + data.networkConnections = res; + functionProcessed(); + }); + } + + memory.mem().then(res => { + data.mem = res; + functionProcessed(); + }); + + if (!_sunos) { + battery().then(res => { + data.battery = res; + functionProcessed(); + }); + } + + if (!_sunos) { + processes.services(srv).then(res => { + data.services = res; + functionProcessed(); + }); + } + + if (!_sunos) { + filesystem.fsSize().then(res => { + data.fsSize = res; + functionProcessed(); + }); + } + + if (!_windows && !_openbsd && !_freebsd && !_netbsd && !_sunos) { + filesystem.fsStats().then(res => { + data.fsStats = res; + functionProcessed(); + }); + } + + if (!_windows && !_openbsd && !_freebsd && !_netbsd && !_sunos) { + filesystem.disksIO().then(res => { + data.disksIO = res; + functionProcessed(); + }); + } + + if (!_openbsd && !_freebsd && !_netbsd && !_sunos) { + wifi.wifiNetworks().then(res => { + data.wifiNetworks = res; + functionProcessed(); + }); + } + + internet.inetLatency().then(res => { + data.inetLatency = res; + functionProcessed(); + }); + }); + }); +} + +// -------------------------- +// get all data at once +// -------------------------- +// 2 additional parameters needed +// - srv: comma separated list of services to monitor e.g. "mysql, apache, postgresql" +// - iface: define network interface for which you like to monitor network speed e.g. "eth0" + +function getAllData(srv, iface, callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + let data = {}; + + if (iface && util.isFunction(iface) && !callback) { + callback = iface; + iface = ''; + } + + if (srv && util.isFunction(srv) && !iface && !callback) { + callback = srv; + srv = ''; + iface = ''; + } + + getStaticData().then(res => { + data = res; + getDynamicData(srv, iface).then(res => { + for (let key in res) { + if ({}.hasOwnProperty.call(res, key)) { + data[key] = res[key]; + } + } + if (callback) { callback(data); } + resolve(data); + }); + }); + }); + }); +} + +function get(valueObject, callback) { + return new Promise((resolve) => { + process.nextTick(() => { + const allPromises = Object.keys(valueObject) + .filter(func => ({}.hasOwnProperty.call(exports, func))) + .map(func => { + const params = valueObject[func].substring(valueObject[func].lastIndexOf('(') + 1, valueObject[func].lastIndexOf(')')); + let funcWithoutParams = func.indexOf(')') >= 0 ? func.split(')')[1].trim() : func; + funcWithoutParams = func.indexOf('|') >= 0 ? func.split('|')[0].trim() : funcWithoutParams; + if (params) { + return exports[funcWithoutParams](params); + } else { + return exports[funcWithoutParams](''); + } + }); + + Promise.all(allPromises).then(data => { + const result = {}; + let i = 0; + for (let key in valueObject) { + if ({}.hasOwnProperty.call(valueObject, key) && {}.hasOwnProperty.call(exports, key) && data.length > i) { + if (valueObject[key] === '*' || valueObject[key] === 'all') { + result[key] = data[i]; + } else { + let keys = valueObject[key]; + // let params = ''; + let filter = ''; + let filterParts = []; + // remove params + if (keys.indexOf(')') >= 0) { + keys = keys.split(')')[1].trim(); + } + // extract filter and remove it from keys + if (keys.indexOf('|') >= 0) { + filter = keys.split('|')[1].trim(); + filterParts = filter.split(':'); + + keys = keys.split('|')[0].trim(); + } + keys = keys.replace(/,/g, ' ').replace(/ +/g, ' ').split(' '); + if (data[i]) { + if (Array.isArray(data[i])) { + // result is in an array, go through all elements of array and pick only the right ones + const partialArray = []; + data[i].forEach(element => { + let partialRes = {}; + if (keys.length === 1 && (keys[0] === '*' || keys[0] === 'all')) { + partialRes = element; + } else { + keys.forEach(k => { + if ({}.hasOwnProperty.call(element, k)) { + partialRes[k] = element[k]; + } + }); + } + // if there is a filter, then just take those elements + if (filter && filterParts.length === 2) { + if ({}.hasOwnProperty.call(partialRes, filterParts[0].trim())) { + const val = partialRes[filterParts[0].trim()]; + if (typeof val == 'number') { + if (val === parseFloat(filterParts[1].trim())) { + partialArray.push(partialRes); + } + } else if (typeof val == 'string') { + if (val.toLowerCase() === filterParts[1].trim().toLowerCase()) { + partialArray.push(partialRes); + } + } + } + } else { + partialArray.push(partialRes); + } + + }); + result[key] = partialArray; + } else { + const partialRes = {}; + keys.forEach(k => { + if ({}.hasOwnProperty.call(data[i], k)) { + partialRes[k] = data[i][k]; + } + }); + result[key] = partialRes; + } + } else { + result[key] = {}; + } + } + i++; + } + } + if (callback) { callback(result); } + resolve(result); + }); + }); + }); +} + +function observe(valueObject, interval, callback) { + let _data = null; + + const result = setInterval(() => { + get(valueObject).then(data => { + if (JSON.stringify(_data) !== JSON.stringify(data)) { + _data = Object.assign({}, data); + callback(data); + } + }); + }, interval); + return result; +} + +// ---------------------------------------------------------------------------------- +// export all libs +// ---------------------------------------------------------------------------------- + +exports.version = version; +exports.system = system.system; +exports.bios = system.bios; +exports.baseboard = system.baseboard; +exports.chassis = system.chassis; + +exports.time = osInfo.time; +exports.osInfo = osInfo.osInfo; +exports.versions = osInfo.versions; +exports.shell = osInfo.shell; +exports.uuid = osInfo.uuid; + +exports.cpu = cpu.cpu; +exports.cpuFlags = cpu.cpuFlags; +exports.cpuCache = cpu.cpuCache; +exports.cpuCurrentSpeed = cpu.cpuCurrentSpeed; +exports.cpuTemperature = cpu.cpuTemperature; +exports.currentLoad = cpu.currentLoad; +exports.fullLoad = cpu.fullLoad; + +exports.mem = memory.mem; +exports.memLayout = memory.memLayout; + +exports.battery = battery; + +exports.graphics = graphics.graphics; + +exports.fsSize = filesystem.fsSize; +exports.fsOpenFiles = filesystem.fsOpenFiles; +exports.blockDevices = filesystem.blockDevices; +exports.fsStats = filesystem.fsStats; +exports.disksIO = filesystem.disksIO; +exports.diskLayout = filesystem.diskLayout; + +exports.networkInterfaceDefault = network.networkInterfaceDefault; +exports.networkGatewayDefault = network.networkGatewayDefault; +exports.networkInterfaces = network.networkInterfaces; +exports.networkStats = network.networkStats; +exports.networkConnections = network.networkConnections; + +exports.wifiNetworks = wifi.wifiNetworks; +exports.wifiInterfaces = wifi.wifiInterfaces; +exports.wifiConnections = wifi.wifiConnections; + +exports.services = processes.services; +exports.processes = processes.processes; +exports.processLoad = processes.processLoad; + +exports.users = users.users; + +exports.inetChecksite = internet.inetChecksite; +exports.inetLatency = internet.inetLatency; + +exports.dockerInfo = docker.dockerInfo; +exports.dockerImages = docker.dockerImages; +exports.dockerContainers = docker.dockerContainers; +exports.dockerContainerStats = docker.dockerContainerStats; +exports.dockerContainerProcesses = docker.dockerContainerProcesses; +exports.dockerVolumes = docker.dockerVolumes; +exports.dockerAll = docker.dockerAll; + +exports.vboxInfo = vbox.vboxInfo; + +exports.printer = printer.printer; + +exports.usb = usb.usb; + +exports.audio = audio.audio; +exports.bluetoothDevices = bluetooth.bluetoothDevices; + +exports.getStaticData = getStaticData; +exports.getDynamicData = getDynamicData; +exports.getAllData = getAllData; +exports.get = get; +exports.observe = observe; + +exports.powerShellStart = util.powerShellStart; +exports.powerShellRelease = util.powerShellRelease; diff --git a/node_modules/systeminformation/lib/internet.js b/node_modules/systeminformation/lib/internet.js new file mode 100644 index 0000000..71f0878 --- /dev/null +++ b/node_modules/systeminformation/lib/internet.js @@ -0,0 +1,240 @@ +'use strict'; +// @ts-check +// ================================================================================== +// internet.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 12. Internet +// ---------------------------------------------------------------------------------- + +// const exec = require('child_process').exec; +const util = require('./util'); + +let _platform = process.platform; + +const _linux = (_platform === 'linux' || _platform === 'android'); +const _darwin = (_platform === 'darwin'); +const _windows = (_platform === 'win32'); +const _freebsd = (_platform === 'freebsd'); +const _openbsd = (_platform === 'openbsd'); +const _netbsd = (_platform === 'netbsd'); +const _sunos = (_platform === 'sunos'); + +// -------------------------- +// check if external site is available + +function inetChecksite(url, callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + let result = { + url: url, + ok: false, + status: 404, + ms: null + }; + if (typeof url !== 'string') { + if (callback) { callback(result); } + return resolve(result); + } + let urlSanitized = ''; + const s = util.sanitizeShellString(url, true); + for (let i = 0; i <= util.mathMin(s.length, 2000); i++) { + if (!(s[i] === undefined)) { + s[i].__proto__.toLowerCase = util.stringToLower; + const sl = s[i].toLowerCase(); + if (sl && sl[0] && !sl[1] && sl[0].length === 1) { + urlSanitized = urlSanitized + sl[0]; + } + } + } + result.url = urlSanitized; + try { + if (urlSanitized && !util.isPrototypePolluted()) { + urlSanitized.__proto__.startsWith = util.stringStartWith; + if (urlSanitized.startsWith('file:') || urlSanitized.startsWith('gopher:') || urlSanitized.startsWith('telnet:') || urlSanitized.startsWith('mailto:') || urlSanitized.startsWith('news:') || urlSanitized.startsWith('nntp:')) { + if (callback) { callback(result); } + return resolve(result); + } + let t = Date.now(); + if (_linux || _freebsd || _openbsd || _netbsd || _darwin || _sunos) { + let args = ['-I', '--connect-timeout', '5', '-m', '5']; + args.push(urlSanitized); + let cmd = 'curl'; + util.execSafe(cmd, args).then((stdout) => { + const lines = stdout.split('\n'); + let statusCode = lines[0] && lines[0].indexOf(' ') >= 0 ? parseInt(lines[0].split(' ')[1], 10) : 404; + result.status = statusCode || 404; + result.ok = (statusCode === 200 || statusCode === 301 || statusCode === 302 || statusCode === 304); + result.ms = (result.ok ? Date.now() - t : null); + if (callback) { callback(result); } + resolve(result); + }); + } + if (_windows) { // if this is stable, this can be used for all OS types + const http = (urlSanitized.startsWith('https:') ? require('https') : require('http')); + try { + http.get(urlSanitized, (res) => { + const statusCode = res.statusCode; + + result.status = statusCode || 404; + result.ok = (statusCode === 200 || statusCode === 301 || statusCode === 302 || statusCode === 304); + + if (statusCode !== 200) { + res.resume(); + result.ms = (result.ok ? Date.now() - t : null); + if (callback) { callback(result); } + resolve(result); + } else { + res.on('data', () => { }); + res.on('end', () => { + result.ms = (result.ok ? Date.now() - t : null); + if (callback) { callback(result); } + resolve(result); + }); + } + }).on('error', () => { + if (callback) { callback(result); } + resolve(result); + }); + } catch (err) { + if (callback) { callback(result); } + resolve(result); + } + } + } else { + if (callback) { callback(result); } + resolve(result); + } + } catch (err) { + if (callback) { callback(result); } + resolve(result); + } + }); + }); +} + +exports.inetChecksite = inetChecksite; + +// -------------------------- +// check inet latency + +function inetLatency(host, callback) { + + // fallback - if only callback is given + if (util.isFunction(host) && !callback) { + callback = host; + host = ''; + } + + host = host || '8.8.8.8'; + + return new Promise((resolve) => { + process.nextTick(() => { + if (typeof host !== 'string') { + if (callback) { callback(null); } + return resolve(null); + } + let hostSanitized = ''; + const s = (util.isPrototypePolluted() ? '8.8.8.8' : util.sanitizeShellString(host, true)).trim(); + for (let i = 0; i <= util.mathMin(s.length, 2000); i++) { + if (!(s[i] === undefined)) { + s[i].__proto__.toLowerCase = util.stringToLower; + const sl = s[i].toLowerCase(); + if (sl && sl[0] && !sl[1]) { + hostSanitized = hostSanitized + sl[0]; + } + } + } + hostSanitized.__proto__.startsWith = util.stringStartWith; + if (hostSanitized.startsWith('file:') || hostSanitized.startsWith('gopher:') || hostSanitized.startsWith('telnet:') || hostSanitized.startsWith('mailto:') || hostSanitized.startsWith('news:') || hostSanitized.startsWith('nntp:')) { + if (callback) { callback(null); } + return resolve(null); + } + let params; + let filt; + if (_linux || _freebsd || _openbsd || _netbsd || _darwin) { + if (_linux) { + params = ['-c', '2', '-w', '3', hostSanitized]; + filt = 'rtt'; + } + if (_freebsd || _openbsd || _netbsd) { + params = ['-c', '2', '-t', '3', hostSanitized]; + filt = 'round-trip'; + } + if (_darwin) { + params = ['-c2', '-t3', hostSanitized]; + filt = 'avg'; + } + util.execSafe('ping', params).then((stdout) => { + let result = null; + if (stdout) { + const lines = stdout.split('\n').filter(line => line.indexOf(filt) >= 0).join('\n'); + + const line = lines.split('='); + if (line.length > 1) { + const parts = line[1].split('/'); + if (parts.length > 1) { + result = parseFloat(parts[1]); + } + } + } + if (callback) { callback(result); } + resolve(result); + }); + } + if (_sunos) { + const params = ['-s', '-a', hostSanitized, '56', '2']; + const filt = 'avg'; + util.execSafe('ping', params, { timeout: 3000 }).then((stdout) => { + let result = null; + if (stdout) { + const lines = stdout.split('\n').filter(line => line.indexOf(filt) >= 0).join('\n'); + const line = lines.split('='); + if (line.length > 1) { + const parts = line[1].split('/'); + if (parts.length > 1) { + result = parseFloat(parts[1].replace(',', '.')); + } + } + } + if (callback) { callback(result); } + resolve(result); + }); + } + if (_windows) { + let result = null; + try { + const params = [hostSanitized, '-n', '1']; + util.execSafe('ping', params, util.execOptsWin).then((stdout) => { + if (stdout) { + let lines = stdout.split('\r\n'); + lines.shift(); + lines.forEach(function (line) { + if ((line.toLowerCase().match(/ms/g) || []).length === 3) { + let l = line.replace(/ +/g, ' ').split(' '); + if (l.length > 6) { + result = parseFloat(l[l.length - 1]); + } + } + }); + } + if (callback) { callback(result); } + resolve(result); + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + }); + }); +} + +exports.inetLatency = inetLatency; diff --git a/node_modules/systeminformation/lib/memory.js b/node_modules/systeminformation/lib/memory.js new file mode 100644 index 0000000..bbf7e5c --- /dev/null +++ b/node_modules/systeminformation/lib/memory.js @@ -0,0 +1,547 @@ +'use strict'; +// @ts-check +// ================================================================================== +// memory.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 5. Memory +// ---------------------------------------------------------------------------------- + +const os = require('os'); +const exec = require('child_process').exec; +const execSync = require('child_process').execSync; +const util = require('./util'); +const fs = require('fs'); + +let _platform = process.platform; + +const _linux = (_platform === 'linux' || _platform === 'android'); +const _darwin = (_platform === 'darwin'); +const _windows = (_platform === 'win32'); +const _freebsd = (_platform === 'freebsd'); +const _openbsd = (_platform === 'openbsd'); +const _netbsd = (_platform === 'netbsd'); +const _sunos = (_platform === 'sunos'); + +const OSX_RAM_manufacturers = { + '0x014F': 'Transcend Information', + '0x2C00': 'Micron Technology Inc.', + '0x802C': 'Micron Technology Inc.', + '0x80AD': 'Hynix Semiconductor Inc.', + '0x80CE': 'Samsung Electronics Inc.', + '0xAD00': 'Hynix Semiconductor Inc.', + '0xCE00': 'Samsung Electronics Inc.', + '0x02FE': 'Elpida', + '0x5105': 'Qimonda AG i. In.', + '0x8551': 'Qimonda AG i. In.', + '0x859B': 'Crucial', + '0x04CD': 'G-Skill' +}; + +const LINUX_RAM_manufacturers = { + '017A': 'Apacer', + '0198': 'HyperX', + '029E': 'Corsair', + '04CB': 'A-DATA', + '04CD': 'G-Skill', + '059B': 'Crucial', + '00CE': 'Samsung', + '1315': 'Crutial', + '014F': 'Transcend Information', + '2C00': 'Micron Technology Inc.', + '802C': 'Micron Technology Inc.', + '80AD': 'Hynix Semiconductor Inc.', + '80CE': 'Samsung Electronics Inc.', + 'AD00': 'Hynix Semiconductor Inc.', + 'CE00': 'Samsung Electronics Inc.', + '02FE': 'Elpida', + '5105': 'Qimonda AG i. In.', + '8551': 'Qimonda AG i. In.', + '859B': 'Crucial' +}; + +// _______________________________________________________________________________________ +// | R A M | H D | +// |______________________|_________________________| | | +// | active buffers/cache | | | +// |________________________________________________|___________|_________|______________| +// | used free | used free | +// |____________________________________________________________|________________________| +// | total | swap | +// |____________________________________________________________|________________________| + +// free (older versions) +// ---------------------------------- +// # free +// total used free shared buffers cached +// Mem: 16038 (1) 15653 (2) 384 (3) 0 (4) 236 (5) 14788 (6) +// -/+ buffers/cache: 628 (7) 15409 (8) +// Swap: 16371 83 16288 +// +// |------------------------------------------------------------| +// | R A M | +// |______________________|_____________________________________| +// | active (2-(5+6) = 7) | available (3+5+6 = 8) | +// |______________________|_________________________|___________| +// | active | buffers/cache (5+6) | | +// |________________________________________________|___________| +// | used (2) | free (3) | +// |____________________________________________________________| +// | total (1) | +// |____________________________________________________________| + +// +// free (since free von procps-ng 3.3.10) +// ---------------------------------- +// # free +// total used free shared buffers/cache available +// Mem: 16038 (1) 628 (2) 386 (3) 0 (4) 15024 (5) 14788 (6) +// Swap: 16371 83 16288 +// +// |------------------------------------------------------------| +// | R A M | +// |______________________|_____________________________________| +// | | available (6) estimated | +// |______________________|_________________________|___________| +// | active (2) | buffers/cache (5) | free (3) | +// |________________________________________________|___________| +// | total (1) | +// |____________________________________________________________| +// +// Reference: http://www.software-architect.net/blog/article/date/2015/06/12/-826c6e5052.html + +// /procs/meminfo - sample (all in kB) +// +// MemTotal: 32806380 kB +// MemFree: 17977744 kB +// MemAvailable: 19768972 kB +// Buffers: 517028 kB +// Cached: 2161876 kB +// SwapCached: 456 kB +// Active: 12081176 kB +// Inactive: 2164616 kB +// Active(anon): 10832884 kB +// Inactive(anon): 1477272 kB +// Active(file): 1248292 kB +// Inactive(file): 687344 kB +// Unevictable: 0 kB +// Mlocked: 0 kB +// SwapTotal: 16768892 kB +// SwapFree: 16768304 kB +// Dirty: 268 kB +// Writeback: 0 kB +// AnonPages: 11568832 kB +// Mapped: 719992 kB +// Shmem: 743272 kB +// Slab: 335716 kB +// SReclaimable: 256364 kB +// SUnreclaim: 79352 kB + +function mem(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + + let result = { + total: os.totalmem(), + free: os.freemem(), + used: os.totalmem() - os.freemem(), + + active: os.totalmem() - os.freemem(), // temporarily (fallback) + available: os.freemem(), // temporarily (fallback) + buffers: 0, + cached: 0, + slab: 0, + buffcache: 0, + + swaptotal: 0, + swapused: 0, + swapfree: 0 + }; + + if (_linux) { + fs.readFile('/proc/meminfo', function (error, stdout) { + if (!error) { + const lines = stdout.toString().split('\n'); + result.total = parseInt(util.getValue(lines, 'memtotal'), 10); + result.total = result.total ? result.total * 1024 : os.totalmem(); + result.free = parseInt(util.getValue(lines, 'memfree'), 10); + result.free = result.free ? result.free * 1024 : os.freemem(); + result.used = result.total - result.free; + + result.buffers = parseInt(util.getValue(lines, 'buffers'), 10); + result.buffers = result.buffers ? result.buffers * 1024 : 0; + result.cached = parseInt(util.getValue(lines, 'cached'), 10); + result.cached = result.cached ? result.cached * 1024 : 0; + result.slab = parseInt(util.getValue(lines, 'slab'), 10); + result.slab = result.slab ? result.slab * 1024 : 0; + result.buffcache = result.buffers + result.cached + result.slab; + + let available = parseInt(util.getValue(lines, 'memavailable'), 10); + result.available = available ? available * 1024 : result.free + result.buffcache; + result.active = result.total - result.available; + + result.swaptotal = parseInt(util.getValue(lines, 'swaptotal'), 10); + result.swaptotal = result.swaptotal ? result.swaptotal * 1024 : 0; + result.swapfree = parseInt(util.getValue(lines, 'swapfree'), 10); + result.swapfree = result.swapfree ? result.swapfree * 1024 : 0; + result.swapused = result.swaptotal - result.swapfree; + } + if (callback) { callback(result); } + resolve(result); + }); + } + if (_freebsd || _openbsd || _netbsd) { + exec('/sbin/sysctl hw.realmem hw.physmem vm.stats.vm.v_page_count vm.stats.vm.v_wire_count vm.stats.vm.v_active_count vm.stats.vm.v_inactive_count vm.stats.vm.v_cache_count vm.stats.vm.v_free_count vm.stats.vm.v_page_size', function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + const pagesize = parseInt(util.getValue(lines, 'vm.stats.vm.v_page_size'), 10); + const inactive = parseInt(util.getValue(lines, 'vm.stats.vm.v_inactive_count'), 10) * pagesize; + const cache = parseInt(util.getValue(lines, 'vm.stats.vm.v_cache_count'), 10) * pagesize; + + result.total = parseInt(util.getValue(lines, 'hw.realmem'), 10); + if (isNaN(result.total)) { result.total = parseInt(util.getValue(lines, 'hw.physmem'), 10); } + result.free = parseInt(util.getValue(lines, 'vm.stats.vm.v_free_count'), 10) * pagesize; + result.buffcache = inactive + cache; + result.available = result.buffcache + result.free; + result.active = result.total - result.free - result.buffcache; + + result.swaptotal = 0; + result.swapfree = 0; + result.swapused = 0; + + } + if (callback) { callback(result); } + resolve(result); + }); + } + if (_sunos) { + if (callback) { callback(result); } + resolve(result); + } + if (_darwin) { + let pageSize = 4096; + try { + let sysPpageSize = util.toInt(execSync('sysctl -n vm.pagesize').toString()); + pageSize = sysPpageSize || pageSize; + } catch (e) { + util.noop(); + } + exec('vm_stat 2>/dev/null | grep "Pages active"', function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + + result.active = parseInt(lines[0].split(':')[1], 10) * pageSize; + result.buffcache = result.used - result.active; + result.available = result.free + result.buffcache; + } + exec('sysctl -n vm.swapusage 2>/dev/null', function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + if (lines.length > 0) { + let line = lines[0].replace(/,/g, '.').replace(/M/g, ''); + line = line.trim().split(' '); + for (let i = 0; i < line.length; i++) { + if (line[i].toLowerCase().indexOf('total') !== -1) { result.swaptotal = parseFloat(line[i].split('=')[1].trim()) * 1024 * 1024; } + if (line[i].toLowerCase().indexOf('used') !== -1) { result.swapused = parseFloat(line[i].split('=')[1].trim()) * 1024 * 1024; } + if (line[i].toLowerCase().indexOf('free') !== -1) { result.swapfree = parseFloat(line[i].split('=')[1].trim()) * 1024 * 1024; } + } + } + } + if (callback) { callback(result); } + resolve(result); + }); + }); + } + if (_windows) { + let swaptotal = 0; + let swapused = 0; + try { + util.powerShell('Get-CimInstance Win32_PageFileUsage | Select AllocatedBaseSize, CurrentUsage').then((stdout, error) => { + if (!error) { + let lines = stdout.split('\r\n').filter(line => line.trim() !== '').filter((line, idx) => idx > 0); + lines.forEach(function (line) { + if (line !== '') { + line = line.trim().split(/\s\s+/); + swaptotal = swaptotal + (parseInt(line[0], 10) || 0); + swapused = swapused + (parseInt(line[1], 10) || 0); + } + }); + } + result.swaptotal = swaptotal * 1024 * 1024; + result.swapused = swapused * 1024 * 1024; + result.swapfree = result.swaptotal - result.swapused; + + if (callback) { callback(result); } + resolve(result); + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + }); + }); +} + +exports.mem = mem; + +function memLayout(callback) { + + function getManufacturerDarwin(manId) { + if ({}.hasOwnProperty.call(OSX_RAM_manufacturers, manId)) { + return (OSX_RAM_manufacturers[manId]); + } + return manId; + } + + function getManufacturerLinux(manId) { + const manIdSearch = manId.replace('0x', '').toUpperCase(); + if (manIdSearch.length === 4 && {}.hasOwnProperty.call(LINUX_RAM_manufacturers, manIdSearch)) { + return (LINUX_RAM_manufacturers[manIdSearch]); + } + return manId; + } + + return new Promise((resolve) => { + process.nextTick(() => { + + let result = []; + + if (_linux || _freebsd || _openbsd || _netbsd) { + exec('export LC_ALL=C; dmidecode -t memory 2>/dev/null | grep -iE "Size:|Type|Speed|Manufacturer|Form Factor|Locator|Memory Device|Serial Number|Voltage|Part Number"; unset LC_ALL', function (error, stdout) { + if (!error) { + let devices = stdout.toString().split('Memory Device'); + devices.shift(); + devices.forEach(function (device) { + let lines = device.split('\n'); + const sizeString = util.getValue(lines, 'Size'); + const size = sizeString.indexOf('GB') >= 0 ? parseInt(sizeString, 10) * 1024 * 1024 * 1024 : parseInt(sizeString, 10) * 1024 * 1024; + if (parseInt(util.getValue(lines, 'Size'), 10) > 0) { + const totalWidth = util.toInt(util.getValue(lines, 'Total Width')); + const dataWidth = util.toInt(util.getValue(lines, 'Data Width')); + result.push({ + size, + bank: util.getValue(lines, 'Bank Locator'), + type: util.getValue(lines, 'Type:'), + ecc: dataWidth && totalWidth ? totalWidth > dataWidth : false, + clockSpeed: (util.getValue(lines, 'Configured Clock Speed:') ? parseInt(util.getValue(lines, 'Configured Clock Speed:'), 10) : (util.getValue(lines, 'Speed:') ? parseInt(util.getValue(lines, 'Speed:'), 10) : null)), + formFactor: util.getValue(lines, 'Form Factor:'), + manufacturer: getManufacturerLinux(util.getValue(lines, 'Manufacturer:')), + partNum: util.getValue(lines, 'Part Number:'), + serialNum: util.getValue(lines, 'Serial Number:'), + voltageConfigured: parseFloat(util.getValue(lines, 'Configured Voltage:')) || null, + voltageMin: parseFloat(util.getValue(lines, 'Minimum Voltage:')) || null, + voltageMax: parseFloat(util.getValue(lines, 'Maximum Voltage:')) || null, + }); + } else { + result.push({ + size: 0, + bank: util.getValue(lines, 'Bank Locator'), + type: 'Empty', + ecc: null, + clockSpeed: 0, + formFactor: util.getValue(lines, 'Form Factor:'), + partNum: '', + serialNum: '', + voltageConfigured: null, + voltageMin: null, + voltageMax: null, + }); + } + }); + } + if (!result.length) { + result.push({ + size: os.totalmem(), + bank: '', + type: '', + ecc: null, + clockSpeed: 0, + formFactor: '', + partNum: '', + serialNum: '', + voltageConfigured: null, + voltageMin: null, + voltageMax: null, + }); + + // Try Raspberry PI + try { + let stdout = execSync('cat /proc/cpuinfo 2>/dev/null'); + let lines = stdout.toString().split('\n'); + let model = util.getValue(lines, 'hardware', ':', true).toUpperCase(); + let version = util.getValue(lines, 'revision', ':', true).toLowerCase(); + + if (model === 'BCM2835' || model === 'BCM2708' || model === 'BCM2709' || model === 'BCM2835' || model === 'BCM2837') { + + const clockSpeed = { + '0': 400, + '1': 450, + '2': 450, + '3': 3200 + }; + result[0].type = 'LPDDR2'; + result[0].type = version && version[2] && version[2] === '3' ? 'LPDDR4' : result[0].type; + result[0].ecc = false; + result[0].clockSpeed = version && version[2] && clockSpeed[version[2]] || 400; + result[0].clockSpeed = version && version[4] && version[4] === 'd' ? 500 : result[0].clockSpeed; + result[0].formFactor = 'SoC'; + + stdout = execSync('vcgencmd get_config sdram_freq 2>/dev/null'); + lines = stdout.toString().split('\n'); + let freq = parseInt(util.getValue(lines, 'sdram_freq', '=', true), 10) || 0; + if (freq) { + result[0].clockSpeed = freq; + } + + stdout = execSync('vcgencmd measure_volts sdram_p 2>/dev/null'); + lines = stdout.toString().split('\n'); + let voltage = parseFloat(util.getValue(lines, 'volt', '=', true)) || 0; + if (voltage) { + result[0].voltageConfigured = voltage; + result[0].voltageMin = voltage; + result[0].voltageMax = voltage; + } + } + } catch (e) { + util.noop(); + } + + } + if (callback) { callback(result); } + resolve(result); + }); + } + + if (_darwin) { + exec('system_profiler SPMemoryDataType', function (error, stdout) { + if (!error) { + const allLines = stdout.toString().split('\n'); + const eccStatus = util.getValue(allLines, 'ecc', ':', true).toLowerCase(); + let devices = stdout.toString().split(' BANK '); + let hasBank = true; + if (devices.length === 1) { + devices = stdout.toString().split(' DIMM'); + hasBank = false; + } + devices.shift(); + devices.forEach(function (device) { + let lines = device.split('\n'); + const bank = (hasBank ? 'BANK ' : 'DIMM') + lines[0].trim().split('/')[0]; + const size = parseInt(util.getValue(lines, ' Size')); + if (size) { + result.push({ + size: size * 1024 * 1024 * 1024, + bank: bank, + type: util.getValue(lines, ' Type:'), + ecc: eccStatus ? eccStatus === 'enabled' : null, + clockSpeed: parseInt(util.getValue(lines, ' Speed:'), 10), + formFactor: '', + manufacturer: getManufacturerDarwin(util.getValue(lines, ' Manufacturer:')), + partNum: util.getValue(lines, ' Part Number:'), + serialNum: util.getValue(lines, ' Serial Number:'), + voltageConfigured: null, + voltageMin: null, + voltageMax: null, + }); + } else { + result.push({ + size: 0, + bank: bank, + type: 'Empty', + ecc: null, + clockSpeed: 0, + formFactor: '', + manufacturer: '', + partNum: '', + serialNum: '', + voltageConfigured: null, + voltageMin: null, + voltageMax: null, + }); + } + }); + } + if (!result.length) { + const lines = stdout.toString().split('\n'); + const size = parseInt(util.getValue(lines, ' Memory:')); + const type = util.getValue(lines, ' Type:'); + if (size && type) { + result.push({ + size: size * 1024 * 1024 * 1024, + bank: '0', + type, + ecc: false, + clockSpeed: 0, + formFactor: '', + manufacturer: 'Apple', + partNum: '', + serialNum: '', + voltageConfigured: null, + voltageMin: null, + voltageMax: null, + }); + + } + } + if (callback) { callback(result); } + resolve(result); + }); + } + if (_sunos) { + if (callback) { callback(result); } + resolve(result); + } + if (_windows) { + const memoryTypes = 'Unknown|Other|DRAM|Synchronous DRAM|Cache DRAM|EDO|EDRAM|VRAM|SRAM|RAM|ROM|FLASH|EEPROM|FEPROM|EPROM|CDRAM|3DRAM|SDRAM|SGRAM|RDRAM|DDR|DDR2|DDR2 FB-DIMM|Reserved|DDR3|FBD2|DDR4|LPDDR|LPDDR2|LPDDR3|LPDDR4'.split('|'); + const FormFactors = 'Unknown|Other|SIP|DIP|ZIP|SOJ|Proprietary|SIMM|DIMM|TSOP|PGA|RIMM|SODIMM|SRIMM|SMD|SSMP|QFP|TQFP|SOIC|LCC|PLCC|BGA|FPBGA|LGA'.split('|'); + + try { + util.powerShell('Get-WmiObject Win32_PhysicalMemory | select DataWidth,TotalWidth,Capacity,BankLabel,MemoryType,SMBIOSMemoryType,ConfiguredClockSpeed,FormFactor,Manufacturer,PartNumber,SerialNumber,ConfiguredVoltage,MinVoltage,MaxVoltage | fl').then((stdout, error) => { + if (!error) { + let devices = stdout.toString().split(/\n\s*\n/); + devices.shift(); + devices.forEach(function (device) { + let lines = device.split('\r\n'); + const dataWidth = util.toInt(util.getValue(lines, 'DataWidth', ':')); + const totalWidth = util.toInt(util.getValue(lines, 'TotalWidth', ':')); + const size = parseInt(util.getValue(lines, 'Capacity', ':'), 10) || 0; + if (size) { + result.push({ + size, + bank: util.getValue(lines, 'BankLabel', ':'), // BankLabel + type: memoryTypes[parseInt(util.getValue(lines, 'MemoryType', ':'), 10) || parseInt(util.getValue(lines, 'SMBIOSMemoryType', ':'), 10)], + ecc: dataWidth && totalWidth ? totalWidth > dataWidth : false, + clockSpeed: parseInt(util.getValue(lines, 'ConfiguredClockSpeed', ':'), 10) || parseInt(util.getValue(lines, 'Speed', ':'), 10) || 0, + formFactor: FormFactors[parseInt(util.getValue(lines, 'FormFactor', ':'), 10) || 0], + manufacturer: util.getValue(lines, 'Manufacturer', ':'), + partNum: util.getValue(lines, 'PartNumber', ':'), + serialNum: util.getValue(lines, 'SerialNumber', ':'), + voltageConfigured: (parseInt(util.getValue(lines, 'ConfiguredVoltage', ':'), 10) || 0) / 1000.0, + voltageMin: (parseInt(util.getValue(lines, 'MinVoltage', ':'), 10) || 0) / 1000.0, + voltageMax: (parseInt(util.getValue(lines, 'MaxVoltage', ':'), 10) || 0) / 1000.0, + }); + } + }); + } + if (callback) { callback(result); } + resolve(result); + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + }); + }); +} + +exports.memLayout = memLayout; + diff --git a/node_modules/systeminformation/lib/network.js b/node_modules/systeminformation/lib/network.js new file mode 100644 index 0000000..ae224bc --- /dev/null +++ b/node_modules/systeminformation/lib/network.js @@ -0,0 +1,1729 @@ +'use strict'; +// @ts-check +// ================================================================================== +// network.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 9. Network +// ---------------------------------------------------------------------------------- + +const os = require('os'); +const exec = require('child_process').exec; +const execSync = require('child_process').execSync; +const fs = require('fs'); +const util = require('./util'); + +let _platform = process.platform; + +const _linux = (_platform === 'linux' || _platform === 'android'); +const _darwin = (_platform === 'darwin'); +const _windows = (_platform === 'win32'); +const _freebsd = (_platform === 'freebsd'); +const _openbsd = (_platform === 'openbsd'); +const _netbsd = (_platform === 'netbsd'); +const _sunos = (_platform === 'sunos'); + +let _network = {}; +let _default_iface = ''; +let _ifaces = {}; +let _dhcpNics = []; +let _networkInterfaces = []; +let _mac = {}; +let pathToIp; + +function getDefaultNetworkInterface() { + + let ifacename = ''; + let ifacenameFirst = ''; + try { + let ifaces = os.networkInterfaces(); + + let scopeid = 9999; + + // fallback - "first" external interface (sorted by scopeid) + for (let dev in ifaces) { + if ({}.hasOwnProperty.call(ifaces, dev)) { + ifaces[dev].forEach(function (details) { + if (details && details.internal === false) { + ifacenameFirst = ifacenameFirst || dev; // fallback if no scopeid + if (details.scopeid && details.scopeid < scopeid) { + ifacename = dev; + scopeid = details.scopeid; + } + } + }); + } + } + ifacename = ifacename || ifacenameFirst || ''; + + if (_windows) { + // https://www.inetdaemon.com/tutorials/internet/ip/routing/default_route.shtml + let defaultIp = ''; + const cmd = 'netstat -r'; + const result = execSync(cmd, util.execOptsWin); + const lines = result.toString().split(os.EOL); + lines.forEach(line => { + line = line.replace(/\s+/g, ' ').trim(); + if (line.indexOf('0.0.0.0 0.0.0.0') > -1 && !(/[a-zA-Z]/.test(line))) { + const parts = line.split(' '); + if (parts.length >= 5) { + defaultIp = parts[parts.length - 2]; + } + } + }); + if (defaultIp) { + for (let dev in ifaces) { + if ({}.hasOwnProperty.call(ifaces, dev)) { + ifaces[dev].forEach(function (details) { + if (details && details.address && details.address === defaultIp) { + ifacename = dev; + } + }); + } + } + } + } + if (_linux) { + let cmd = 'ip route 2> /dev/null | grep default'; + let result = execSync(cmd); + let parts = result.toString().split('\n')[0].split(/\s+/); + if (parts[0] === 'none' && parts[5]) { + ifacename = parts[5]; + } else if (parts[4]) { + ifacename = parts[4]; + } + + if (ifacename.indexOf(':') > -1) { + ifacename = ifacename.split(':')[1].trim(); + } + } + if (_darwin || _freebsd || _openbsd || _netbsd || _sunos) { + let cmd = ''; + if (_linux) { cmd = 'ip route 2> /dev/null | grep default | awk \'{print $5}\''; } + if (_darwin) { cmd = 'route -n get default 2>/dev/null | grep interface: | awk \'{print $2}\''; } + if (_freebsd || _openbsd || _netbsd || _sunos) { cmd = 'route get 0.0.0.0 | grep interface:'; } + // console.log('SYNC - default darwin 3'); + let result = execSync(cmd); + ifacename = result.toString().split('\n')[0]; + if (ifacename.indexOf(':') > -1) { + ifacename = ifacename.split(':')[1].trim(); + } + } + } catch (e) { + util.noop(); + } + if (ifacename) { _default_iface = ifacename; } + return _default_iface; +} + +exports.getDefaultNetworkInterface = getDefaultNetworkInterface; + +function getMacAddresses() { + let iface = ''; + let mac = ''; + let result = {}; + if (_linux || _freebsd || _openbsd || _netbsd) { + if (typeof pathToIp === 'undefined') { + try { + const lines = execSync('which ip').toString().split('\n'); + if (lines.length && lines[0].indexOf(':') === -1 && lines[0].indexOf('/') === 0) { + pathToIp = lines[0]; + } else { + pathToIp = ''; + } + } catch (e) { + pathToIp = ''; + } + } + try { + const cmd = 'export LC_ALL=C; ' + ((pathToIp) ? pathToIp + ' link show up' : '/sbin/ifconfig') + '; unset LC_ALL'; + let res = execSync(cmd); + const lines = res.toString().split('\n'); + for (let i = 0; i < lines.length; i++) { + if (lines[i] && lines[i][0] !== ' ') { + if (pathToIp) { + let nextline = lines[i + 1].trim().split(' '); + if (nextline[0] === 'link/ether') { + iface = lines[i].split(' ')[1]; + iface = iface.slice(0, iface.length - 1); + mac = nextline[1]; + } + } else { + iface = lines[i].split(' ')[0]; + mac = lines[i].split('HWaddr ')[1]; + } + + if (iface && mac) { + result[iface] = mac.trim(); + iface = ''; + mac = ''; + } + } + } + } catch (e) { + util.noop(); + } + } + if (_darwin) { + try { + const cmd = '/sbin/ifconfig'; + // console.log('SYNC - macAde darwin 6'); + let res = execSync(cmd); + const lines = res.toString().split('\n'); + for (let i = 0; i < lines.length; i++) { + if (lines[i] && lines[i][0] !== '\t' && lines[i].indexOf(':') > 0) { + iface = lines[i].split(':')[0]; + } else if (lines[i].indexOf('\tether ') === 0) { + mac = lines[i].split('\tether ')[1]; + if (iface && mac) { + result[iface] = mac.trim(); + iface = ''; + mac = ''; + } + } + } + } catch (e) { + util.noop(); + } + } + return result; +} + +function networkInterfaceDefault(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + let result = getDefaultNetworkInterface(); + if (callback) { callback(result); } + resolve(result); + }); + }); +} + +exports.networkInterfaceDefault = networkInterfaceDefault; + +// -------------------------- +// NET - interfaces + +function parseLinesWindowsNics(sections, nconfigsections) { + let nics = []; + for (let i in sections) { + if ({}.hasOwnProperty.call(sections, i)) { + + if (sections[i].trim() !== '') { + + let lines = sections[i].trim().split('\r\n'); + let linesNicConfig = nconfigsections && nconfigsections[i] ? nconfigsections[i].trim().split('\r\n') : []; + let netEnabled = util.getValue(lines, 'NetEnabled', ':'); + let adapterType = util.getValue(lines, 'AdapterTypeID', ':') === '9' ? 'wireless' : 'wired'; + let ifacename = util.getValue(lines, 'Name', ':').replace(/\]/g, ')').replace(/\[/g, '('); + let iface = util.getValue(lines, 'NetConnectionID', ':').replace(/\]/g, ')').replace(/\[/g, '('); + if (ifacename.toLowerCase().indexOf('wi-fi') >= 0 || ifacename.toLowerCase().indexOf('wireless') >= 0) { + adapterType = 'wireless'; + } + if (netEnabled !== '') { + const speed = parseInt(util.getValue(lines, 'speed', ':').trim(), 10) / 1000000; + nics.push({ + mac: util.getValue(lines, 'MACAddress', ':').toLowerCase(), + dhcp: util.getValue(linesNicConfig, 'dhcpEnabled', ':').toLowerCase() === 'true', + name: ifacename, + iface, + netEnabled: netEnabled === 'TRUE', + speed: isNaN(speed) ? null : speed, + operstate: util.getValue(lines, 'NetConnectionStatus', ':') === '2' ? 'up' : 'down', + type: adapterType + }); + } + } + } + } + return nics; +} + +function getWindowsNics() { + // const cmd = util.getWmic() + ' nic get /value'; + // const cmdnicconfig = util.getWmic() + ' nicconfig get dhcpEnabled /value'; + return new Promise((resolve) => { + process.nextTick(() => { + let cmd = 'Get-WmiObject Win32_NetworkAdapter | fl *' + '; echo \'#-#-#-#\';'; + cmd += 'Get-WmiObject Win32_NetworkAdapterConfiguration | fl DHCPEnabled' + ''; + try { + util.powerShell(cmd).then(data => { + data = data.split('#-#-#-#'); + const nsections = (data[0] || '').split(/\n\s*\n/); + const nconfigsections = (data[1] || '').split(/\n\s*\n/); + resolve(parseLinesWindowsNics(nsections, nconfigsections)); + }); + } catch (e) { + resolve([]); + } + }); + }); +} + +function getWindowsDNSsuffixes() { + + let iface = {}; + + let dnsSuffixes = { + primaryDNS: '', + exitCode: 0, + ifaces: [], + }; + + try { + const ipconfig = execSync('ipconfig /all', util.execOptsWin); + const ipconfigArray = ipconfig.split('\r\n\r\n'); + + ipconfigArray.forEach((element, index) => { + + if (index == 1) { + const longPrimaryDNS = element.split('\r\n').filter((element) => { + return element.toUpperCase().includes('DNS'); + }); + const primaryDNS = longPrimaryDNS[0].substring(longPrimaryDNS[0].lastIndexOf(':') + 1); + dnsSuffixes.primaryDNS = primaryDNS.trim(); + if (!dnsSuffixes.primaryDNS) { dnsSuffixes.primaryDNS = 'Not defined'; } + } + if (index > 1) { + if (index % 2 == 0) { + const name = element.substring(element.lastIndexOf(' ') + 1).replace(':', ''); + iface.name = name; + } else { + const connectionSpecificDNS = element.split('\r\n').filter((element) => { + return element.toUpperCase().includes('DNS'); + }); + const dnsSuffix = connectionSpecificDNS[0].substring(connectionSpecificDNS[0].lastIndexOf(':') + 1); + iface.dnsSuffix = dnsSuffix.trim(); + dnsSuffixes.ifaces.push(iface); + iface = {}; + } + } + }); + + return dnsSuffixes; + } catch (error) { + // console.log('An error occurred trying to bring the Connection-specific DNS suffix', error.message); + return { + primaryDNS: '', + exitCode: 0, + ifaces: [], + }; + } +} + +function getWindowsIfaceDNSsuffix(ifaces, ifacename) { + let dnsSuffix = ''; + // Adding (.) to ensure ifacename compatibility when duplicated iface-names + const interfaceName = ifacename + '.'; + try { + const connectionDnsSuffix = ifaces.filter((iface) => { + return interfaceName.includes(iface.name + '.'); + }).map((iface) => iface.dnsSuffix); + if (connectionDnsSuffix[0]) { + dnsSuffix = connectionDnsSuffix[0]; + } + if (!dnsSuffix) { dnsSuffix = ''; } + return dnsSuffix; + } catch (error) { + // console.log('Error getting Connection-specific DNS suffix: ', error.message); + return 'Unknown'; + } +} + +function getWindowsWiredProfilesInformation() { + try { + const result = execSync('netsh lan show profiles', util.execOptsWin); + const profileList = result.split('\r\nProfile on interface'); + return profileList; + } catch (error) { + if (error.status === 1 && error.stdout.includes('AutoConfig')) { + return 'Disabled'; + } + return []; + } +} + +function getWindowsWirelessIfaceSSID(interfaceName) { + try { + const result = execSync(`netsh wlan show interface name="${interfaceName}" | findstr "SSID"`, util.execOptsWin); + const SSID = result.split('\r\n').shift(); + const parseSSID = SSID.split(':').pop(); + return parseSSID; + } catch (error) { + return 'Unknown'; + } +} +function getWindowsIEEE8021x(connectionType, iface, ifaces) { + let i8021x = { + state: 'Unknown', + protocol: 'Unknown', + }; + + if (ifaces === 'Disabled') { + i8021x.state = 'Disabled'; + i8021x.protocol = 'Not defined'; + return i8021x; + } + + if (connectionType == 'wired' && ifaces.length > 0) { + try { + // Get 802.1x information by interface name + const iface8021xInfo = ifaces.find((element) => { + return element.includes(iface + '\r\n'); + }); + const arrayIface8021xInfo = iface8021xInfo.split('\r\n'); + const state8021x = arrayIface8021xInfo.find((element) => { + return element.includes('802.1x'); + }); + + if (state8021x.includes('Disabled')) { + i8021x.state = 'Disabled'; + i8021x.protocol = 'Not defined'; + } else if (state8021x.includes('Enabled')) { + const protocol8021x = arrayIface8021xInfo.find((element) => { + return element.includes('EAP'); + }); + i8021x.protocol = protocol8021x.split(':').pop(); + i8021x.state = 'Enabled'; + } + } catch (error) { + // console.log('Error getting wired information:', error); + return i8021x; + } + } else if (connectionType == 'wireless') { + + let i8021xState = ''; + let i8021xProtocol = ''; + + + + try { + const SSID = getWindowsWirelessIfaceSSID(iface); + if (SSID !== 'Unknown') { + i8021xState = execSync(`netsh wlan show profiles "${SSID}" | findstr "802.1X"`, util.execOptsWin); + i8021xProtocol = execSync(`netsh wlan show profiles "${SSID}" | findstr "EAP"`, util.execOptsWin); + } + + if (i8021xState.includes(':') && i8021xProtocol.includes(':')) { + i8021x.state = i8021xState.split(':').pop(); + i8021x.protocol = i8021xProtocol.split(':').pop(); + } + } catch (error) { + // console.log('Error getting wireless information:', error); + if (error.status === 1 && error.stdout.includes('AutoConfig')) { + i8021x.state = 'Disabled'; + i8021x.protocol = 'Not defined'; + } + return i8021x; + } + } + + return i8021x; +} + +function splitSectionsNics(lines) { + const result = []; + let section = []; + lines.forEach(function (line) { + if (!line.startsWith('\t') && !line.startsWith(' ')) { + if (section.length) { + result.push(section); + section = []; + } + } + section.push(line); + }); + if (section.length) { + result.push(section); + } + return result; +} + +function parseLinesDarwinNics(sections) { + let nics = []; + sections.forEach(section => { + let nic = { + iface: '', + mtu: null, + mac: '', + ip6: '', + ip4: '', + speed: null, + type: '', + operstate: '', + duplex: '', + internal: false + }; + const first = section[0]; + nic.iface = first.split(':')[0].trim(); + let parts = first.split('> mtu'); + nic.mtu = parts.length > 1 ? parseInt(parts[1], 10) : null; + if (isNaN(nic.mtu)) { + nic.mtu = null; + } + nic.internal = parts[0].toLowerCase().indexOf('loopback') > -1; + section.forEach(line => { + if (line.trim().startsWith('ether ')) { + nic.mac = line.split('ether ')[1].toLowerCase().trim(); + } + if (line.trim().startsWith('inet6 ') && !nic.ip6) { + nic.ip6 = line.split('inet6 ')[1].toLowerCase().split('%')[0].split(' ')[0]; + } + if (line.trim().startsWith('inet ') && !nic.ip4) { + nic.ip4 = line.split('inet ')[1].toLowerCase().split(' ')[0]; + } + }); + let speed = util.getValue(section, 'link rate'); + nic.speed = speed ? parseFloat(speed) : null; + if (nic.speed === null) { + speed = util.getValue(section, 'uplink rate'); + nic.speed = speed ? parseFloat(speed) : null; + if (nic.speed !== null && speed.toLowerCase().indexOf('gbps') >= 0) { + nic.speed = nic.speed * 1000; + } + } else { + if (speed.toLowerCase().indexOf('gbps') >= 0) { + nic.speed = nic.speed * 1000; + } + } + nic.type = util.getValue(section, 'type').toLowerCase().indexOf('wi-fi') > -1 ? 'wireless' : 'wired'; + nic.operstate = util.getValue(section, 'status').toLowerCase().indexOf('active') > -1 ? 'up' : 'down'; + nic.duplex = util.getValue(section, 'media').toLowerCase().indexOf('half-duplex') > -1 ? 'half' : 'full'; + if (nic.ip6 || nic.ip4 || nic.mac) { + nics.push(nic); + } + }); + return nics; +} + +function getDarwinNics() { + const cmd = '/sbin/ifconfig -v'; + try { + // console.log('SYNC - Nics darwin 12'); + const lines = execSync(cmd, { maxBuffer: 1024 * 20000 }).toString().split('\n'); + const nsections = splitSectionsNics(lines); + return (parseLinesDarwinNics(nsections)); + } catch (e) { + return []; + } +} + +function getLinuxIfaceConnectionName(interfaceName) { + const cmd = `nmcli device status 2>/dev/null | grep ${interfaceName}`; + + try { + const result = execSync(cmd).toString(); + const resultFormat = result.replace(/\s+/g, ' ').trim(); + const connectionNameLines = resultFormat.split(' ').slice(3); + const connectionName = connectionNameLines.join(' '); + return connectionName != '--' ? connectionName : ''; + } catch (e) { + return ''; + } +} + +function checkLinuxDCHPInterfaces(file) { + let result = []; + try { + let cmd = `cat ${file} 2> /dev/null | grep 'iface\\|source'`; + const lines = execSync(cmd, { maxBuffer: 1024 * 20000 }).toString().split('\n'); + + lines.forEach(line => { + const parts = line.replace(/\s+/g, ' ').trim().split(' '); + if (parts.length >= 4) { + if (line.toLowerCase().indexOf(' inet ') >= 0 && line.toLowerCase().indexOf('dhcp') >= 0) { + result.push(parts[1]); + } + } + if (line.toLowerCase().includes('source')) { + let file = line.split(' ')[1]; + result = result.concat(checkLinuxDCHPInterfaces(file)); + } + }); + } catch (e) { + util.noop(); + } + return result; +} + +function getLinuxDHCPNics() { + // alternate methods getting interfaces using DHCP + let cmd = 'ip a 2> /dev/null'; + let result = []; + try { + const lines = execSync(cmd, { maxBuffer: 1024 * 20000 }).toString().split('\n'); + const nsections = splitSectionsNics(lines); + result = (parseLinuxDHCPNics(nsections)); + } catch (e) { + util.noop(); + } + try { + result = checkLinuxDCHPInterfaces('/etc/network/interfaces'); + } catch (e) { + util.noop(); + } + return result; +} + +function parseLinuxDHCPNics(sections) { + const result = []; + if (sections && sections.length) { + sections.forEach(lines => { + if (lines && lines.length) { + const parts = lines[0].split(':'); + if (parts.length > 2) { + for (let line of lines) { + if (line.indexOf(' inet ') >= 0 && line.indexOf(' dynamic ') >= 0) { + const parts2 = line.split(' '); + const nic = parts2[parts2.length - 1].trim(); + result.push(nic); + break; + } + } + } + } + }); + } + return result; +} + +function getLinuxIfaceDHCPstatus(iface, connectionName, DHCPNics) { + let result = false; + if (connectionName) { + const cmd = `nmcli connection show "${connectionName}" 2>/dev/null | grep ipv4.method;`; + try { + const lines = execSync(cmd).toString(); + const resultFormat = lines.replace(/\s+/g, ' ').trim(); + + let dhcStatus = resultFormat.split(' ').slice(1).toString(); + switch (dhcStatus) { + case 'auto': + result = true; + break; + + default: + result = false; + break; + } + return result; + } catch (e) { + return (DHCPNics.indexOf(iface) >= 0); + } + } else { + return (DHCPNics.indexOf(iface) >= 0); + } +} + +function getDarwinIfaceDHCPstatus(iface) { + let result = false; + const cmd = `ipconfig getpacket "${iface}" 2>/dev/null | grep lease_time;`; + try { + // console.log('SYNC - DHCP status darwin 17'); + const lines = execSync(cmd).toString().split('\n'); + if (lines.length && lines[0].startsWith('lease_time')) { + result = true; + } + } catch (e) { + util.noop(); + } + return result; +} + +function getLinuxIfaceDNSsuffix(connectionName) { + if (connectionName) { + const cmd = `nmcli connection show "${connectionName}" 2>/dev/null | grep ipv4.dns-search;`; + try { + const result = execSync(cmd).toString(); + const resultFormat = result.replace(/\s+/g, ' ').trim(); + const dnsSuffix = resultFormat.split(' ').slice(1).toString(); + return dnsSuffix == '--' ? 'Not defined' : dnsSuffix; + } catch (e) { + return 'Unknown'; + } + } else { + return 'Unknown'; + } +} + +function getLinuxIfaceIEEE8021xAuth(connectionName) { + if (connectionName) { + const cmd = `nmcli connection show "${connectionName}" 2>/dev/null | grep 802-1x.eap;`; + try { + const result = execSync(cmd).toString(); + const resultFormat = result.replace(/\s+/g, ' ').trim(); + const authenticationProtocol = resultFormat.split(' ').slice(1).toString(); + + + return authenticationProtocol == '--' ? '' : authenticationProtocol; + } catch (e) { + return 'Not defined'; + } + } else { + return 'Not defined'; + } +} + +function getLinuxIfaceIEEE8021xState(authenticationProtocol) { + if (authenticationProtocol) { + if (authenticationProtocol == 'Not defined') { + return 'Disabled'; + } + return 'Enabled'; + } else { + return 'Unknown'; + } +} + +function testVirtualNic(iface, ifaceName, mac) { + const virtualMacs = ['00:00:00:00:00:00', '00:03:FF', '00:05:69', '00:0C:29', '00:0F:4B', '00:0F:4B', '00:13:07', '00:13:BE', '00:15:5d', '00:16:3E', '00:1C:42', '00:21:F6', '00:21:F6', '00:24:0B', '00:24:0B', '00:50:56', '00:A0:B1', '00:E0:C8', '08:00:27', '0A:00:27', '18:92:2C', '16:DF:49', '3C:F3:92', '54:52:00', 'FC:15:97']; + if (mac) { + return virtualMacs.filter(item => { return mac.toUpperCase().toUpperCase().startsWith(item.substr(0, mac.length)); }).length > 0 || + iface.toLowerCase().indexOf(' virtual ') > -1 || + ifaceName.toLowerCase().indexOf(' virtual ') > -1 || + iface.toLowerCase().indexOf('vethernet ') > -1 || + ifaceName.toLowerCase().indexOf('vethernet ') > -1 || + iface.toLowerCase().startsWith('veth') || + ifaceName.toLowerCase().startsWith('veth') || + iface.toLowerCase().startsWith('vboxnet') || + ifaceName.toLowerCase().startsWith('vboxnet'); + } else { return false; } +} + +function networkInterfaces(callback, rescan, defaultString) { + + if (typeof callback === 'string') { + defaultString = callback; + rescan = true; + callback = null; + } + + if (typeof callback === 'boolean') { + rescan = callback; + callback = null; + defaultString = ''; + } + if (typeof rescan === 'undefined') { + rescan = true; + } + defaultString = defaultString || ''; + defaultString = '' + defaultString; + + return new Promise((resolve) => { + process.nextTick(() => { + + let ifaces = os.networkInterfaces(); + + let result = []; + let nics = []; + let dnsSuffixes = []; + let nics8021xInfo = []; + // seperate handling in OSX + if (_darwin || _freebsd || _openbsd || _netbsd) { + if ((JSON.stringify(ifaces) === JSON.stringify(_ifaces)) && !rescan) { + // no changes - just return object + result = _networkInterfaces; + + if (callback) { callback(result); } + resolve(result); + } else { + const defaultInterface = getDefaultNetworkInterface(); + _ifaces = JSON.parse(JSON.stringify(ifaces)); + + nics = getDarwinNics(); + + + nics.forEach(nic => { + + if ({}.hasOwnProperty.call(ifaces, nic.iface)) { + ifaces[nic.iface].forEach(function (details) { + if (details.family === 'IPv4' || details.family === 4) { + nic.ip4subnet = details.netmask; + } + if (details.family === 'IPv6' || details.family === 6) { + nic.ip6subnet = details.netmask; + } + }); + } + + result.push({ + iface: nic.iface, + ifaceName: nic.iface, + default: nic.iface === defaultInterface, + ip4: nic.ip4, + ip4subnet: nic.ip4subnet || '', + ip6: nic.ip6, + ip6subnet: nic.ip6subnet || '', + mac: nic.mac, + internal: nic.internal, + virtual: nic.internal ? false : testVirtualNic(nic.iface, nic.iface, nic.mac), + operstate: nic.operstate, + type: nic.type, + duplex: nic.duplex, + mtu: nic.mtu, + speed: nic.speed, + dhcp: getDarwinIfaceDHCPstatus(nic.iface), + dnsSuffix: '', + ieee8021xAuth: '', + ieee8021xState: '', + carrierChanges: 0 + }); + }); + _networkInterfaces = result; + if (defaultString.toLowerCase().indexOf('default') >= 0) { + result = result.filter(item => item.default); + if (result.length > 0) { + result = result[0]; + } else { + result = []; + } + } + if (callback) { callback(result); } + resolve(result); + } + } + if (_linux) { + if ((JSON.stringify(ifaces) === JSON.stringify(_ifaces)) && !rescan) { + // no changes - just return object + result = _networkInterfaces; + + if (callback) { callback(result); } + resolve(result); + } else { + _ifaces = JSON.parse(JSON.stringify(ifaces)); + _dhcpNics = getLinuxDHCPNics(); + const defaultInterface = getDefaultNetworkInterface(); + for (let dev in ifaces) { + let ip4 = ''; + let ip4subnet = ''; + let ip6 = ''; + let ip6subnet = ''; + let mac = ''; + let duplex = ''; + let mtu = ''; + let speed = null; + let carrierChanges = 0; + let dhcp = false; + let dnsSuffix = ''; + let ieee8021xAuth = ''; + let ieee8021xState = ''; + let type = ''; + + if ({}.hasOwnProperty.call(ifaces, dev)) { + let ifaceName = dev; + ifaces[dev].forEach(function (details) { + if (details.family === 'IPv4' || details.family === 4) { + ip4 = details.address; + ip4subnet = details.netmask; + } + if (details.family === 'IPv6' || details.family === 6) { + if (!ip6 || ip6.match(/^fe80::/i)) { + ip6 = details.address; + ip6subnet = details.netmask; + } + } + mac = details.mac; + // fallback due to https://github.com/nodejs/node/issues/13581 (node 8.1 - node 8.2) + const nodeMainVersion = parseInt(process.versions.node.split('.'), 10); + if (mac.indexOf('00:00:0') > -1 && (_linux || _darwin) && (!details.internal) && nodeMainVersion >= 8 && nodeMainVersion <= 11) { + if (Object.keys(_mac).length === 0) { + _mac = getMacAddresses(); + } + mac = _mac[dev] || ''; + } + }); + let iface = dev.split(':')[0].trim().toLowerCase(); + const cmd = `echo -n "addr_assign_type: "; cat /sys/class/net/${iface}/addr_assign_type 2>/dev/null; echo; + echo -n "address: "; cat /sys/class/net/${iface}/address 2>/dev/null; echo; + echo -n "addr_len: "; cat /sys/class/net/${iface}/addr_len 2>/dev/null; echo; + echo -n "broadcast: "; cat /sys/class/net/${iface}/broadcast 2>/dev/null; echo; + echo -n "carrier: "; cat /sys/class/net/${iface}/carrier 2>/dev/null; echo; + echo -n "carrier_changes: "; cat /sys/class/net/${iface}/carrier_changes 2>/dev/null; echo; + echo -n "dev_id: "; cat /sys/class/net/${iface}/dev_id 2>/dev/null; echo; + echo -n "dev_port: "; cat /sys/class/net/${iface}/dev_port 2>/dev/null; echo; + echo -n "dormant: "; cat /sys/class/net/${iface}/dormant 2>/dev/null; echo; + echo -n "duplex: "; cat /sys/class/net/${iface}/duplex 2>/dev/null; echo; + echo -n "flags: "; cat /sys/class/net/${iface}/flags 2>/dev/null; echo; + echo -n "gro_flush_timeout: "; cat /sys/class/net/${iface}/gro_flush_timeout 2>/dev/null; echo; + echo -n "ifalias: "; cat /sys/class/net/${iface}/ifalias 2>/dev/null; echo; + echo -n "ifindex: "; cat /sys/class/net/${iface}/ifindex 2>/dev/null; echo; + echo -n "iflink: "; cat /sys/class/net/${iface}/iflink 2>/dev/null; echo; + echo -n "link_mode: "; cat /sys/class/net/${iface}/link_mode 2>/dev/null; echo; + echo -n "mtu: "; cat /sys/class/net/${iface}/mtu 2>/dev/null; echo; + echo -n "netdev_group: "; cat /sys/class/net/${iface}/netdev_group 2>/dev/null; echo; + echo -n "operstate: "; cat /sys/class/net/${iface}/operstate 2>/dev/null; echo; + echo -n "proto_down: "; cat /sys/class/net/${iface}/proto_down 2>/dev/null; echo; + echo -n "speed: "; cat /sys/class/net/${iface}/speed 2>/dev/null; echo; + echo -n "tx_queue_len: "; cat /sys/class/net/${iface}/tx_queue_len 2>/dev/null; echo; + echo -n "type: "; cat /sys/class/net/${iface}/type 2>/dev/null; echo; + echo -n "wireless: "; cat /proc/net/wireless 2>/dev/null | grep ${iface}; echo; + echo -n "wirelessspeed: "; iw dev ${iface} link 2>&1 | grep bitrate; echo;`; + + let lines = []; + try { + lines = execSync(cmd).toString().split('\n'); + const connectionName = getLinuxIfaceConnectionName(iface); + dhcp = getLinuxIfaceDHCPstatus(iface, connectionName, _dhcpNics); + dnsSuffix = getLinuxIfaceDNSsuffix(connectionName); + ieee8021xAuth = getLinuxIfaceIEEE8021xAuth(connectionName); + ieee8021xState = getLinuxIfaceIEEE8021xState(ieee8021xAuth); + } catch (e) { + util.noop(); + } + duplex = util.getValue(lines, 'duplex'); + duplex = duplex.startsWith('cat') ? '' : duplex; + mtu = parseInt(util.getValue(lines, 'mtu'), 10); + let myspeed = parseInt(util.getValue(lines, 'speed'), 10); + speed = isNaN(myspeed) ? null : myspeed; + let wirelessspeed = util.getValue(lines, 'wirelessspeed').split('tx bitrate: '); + if (speed === null && wirelessspeed.length === 2) { + myspeed = parseFloat(wirelessspeed[1]); + speed = isNaN(myspeed) ? null : myspeed; + } + carrierChanges = parseInt(util.getValue(lines, 'carrier_changes'), 10); + const operstate = util.getValue(lines, 'operstate'); + type = operstate === 'up' ? (util.getValue(lines, 'wireless').trim() ? 'wireless' : 'wired') : 'unknown'; + if (iface === 'lo' || iface.startsWith('bond')) { type = 'virtual'; } + + let internal = (ifaces[dev] && ifaces[dev][0]) ? ifaces[dev][0].internal : false; + if (dev.toLowerCase().indexOf('loopback') > -1 || ifaceName.toLowerCase().indexOf('loopback') > -1) { + internal = true; + } + const virtual = internal ? false : testVirtualNic(dev, ifaceName, mac); + result.push({ + iface, + ifaceName, + default: iface === defaultInterface, + ip4, + ip4subnet, + ip6, + ip6subnet, + mac, + internal, + virtual, + operstate, + type, + duplex, + mtu, + speed, + dhcp, + dnsSuffix, + ieee8021xAuth, + ieee8021xState, + carrierChanges, + }); + } + } + _networkInterfaces = result; + if (defaultString.toLowerCase().indexOf('default') >= 0) { + result = result.filter(item => item.default); + if (result.length > 0) { + result = result[0]; + } else { + result = []; + } + } + if (callback) { callback(result); } + resolve(result); + } + } + if (_windows) { + if ((JSON.stringify(ifaces) === JSON.stringify(_ifaces)) && !rescan) { + // no changes - just return object + result = _networkInterfaces; + + if (callback) { callback(result); } + resolve(result); + } else { + _ifaces = JSON.parse(JSON.stringify(ifaces)); + const defaultInterface = getDefaultNetworkInterface(); + + getWindowsNics().then(function (nics) { + nics.forEach(nic => { + let found = false; + Object.keys(ifaces).forEach(key => { + if (!found) { + ifaces[key].forEach(value => { + if (Object.keys(value).indexOf('mac') >= 0) { + found = value['mac'] === nic.mac; + } + }); + } + }); + + if (!found) { + ifaces[nic.name] = [{ mac: nic.mac }]; + } + }); + nics8021xInfo = getWindowsWiredProfilesInformation(); + dnsSuffixes = getWindowsDNSsuffixes(); + for (let dev in ifaces) { + let iface = dev; + let ip4 = ''; + let ip4subnet = ''; + let ip6 = ''; + let ip6subnet = ''; + let mac = ''; + let duplex = ''; + let mtu = ''; + let speed = null; + let carrierChanges = 0; + let operstate = 'down'; + let dhcp = false; + let dnsSuffix = ''; + let ieee8021xAuth = ''; + let ieee8021xState = ''; + let type = ''; + + if ({}.hasOwnProperty.call(ifaces, dev)) { + let ifaceName = dev; + ifaces[dev].forEach(function (details) { + if (details.family === 'IPv4' || details.family === 4) { + ip4 = details.address; + ip4subnet = details.netmask; + } + if (details.family === 'IPv6' || details.family === 6) { + if (!ip6 || ip6.match(/^fe80::/i)) { + ip6 = details.address; + ip6subnet = details.netmask; + } + } + mac = details.mac; + // fallback due to https://github.com/nodejs/node/issues/13581 (node 8.1 - node 8.2) + const nodeMainVersion = parseInt(process.versions.node.split('.'), 10); + if (mac.indexOf('00:00:0') > -1 && (_linux || _darwin) && (!details.internal) && nodeMainVersion >= 8 && nodeMainVersion <= 11) { + if (Object.keys(_mac).length === 0) { + _mac = getMacAddresses(); + } + mac = _mac[dev] || ''; + } + }); + + + + dnsSuffix = getWindowsIfaceDNSsuffix(dnsSuffixes.ifaces, dev); + let foundFirst = false; + nics.forEach(detail => { + if (detail.mac === mac && !foundFirst) { + iface = detail.iface || iface; + ifaceName = detail.name; + dhcp = detail.dhcp; + operstate = detail.operstate; + speed = detail.speed; + type = detail.type; + foundFirst = true; + } + }); + + if (dev.toLowerCase().indexOf('wlan') >= 0 || ifaceName.toLowerCase().indexOf('wlan') >= 0 || ifaceName.toLowerCase().indexOf('802.11n') >= 0 || ifaceName.toLowerCase().indexOf('wireless') >= 0 || ifaceName.toLowerCase().indexOf('wi-fi') >= 0 || ifaceName.toLowerCase().indexOf('wifi') >= 0) { + type = 'wireless'; + } + + const IEEE8021x = getWindowsIEEE8021x(type, dev, nics8021xInfo); + ieee8021xAuth = IEEE8021x.protocol; + ieee8021xState = IEEE8021x.state; + let internal = (ifaces[dev] && ifaces[dev][0]) ? ifaces[dev][0].internal : false; + if (dev.toLowerCase().indexOf('loopback') > -1 || ifaceName.toLowerCase().indexOf('loopback') > -1) { + internal = true; + } + const virtual = internal ? false : testVirtualNic(dev, ifaceName, mac); + result.push({ + iface, + ifaceName, + default: iface === defaultInterface, + ip4, + ip4subnet, + ip6, + ip6subnet, + mac, + internal, + virtual, + operstate, + type, + duplex, + mtu, + speed, + dhcp, + dnsSuffix, + ieee8021xAuth, + ieee8021xState, + carrierChanges, + }); + } + } + _networkInterfaces = result; + if (defaultString.toLowerCase().indexOf('default') >= 0) { + result = result.filter(item => item.default); + if (result.length > 0) { + result = result[0]; + } else { + result = []; + } + } + if (callback) { callback(result); } + resolve(result); + }); + } + } + }); + }); +} + +exports.networkInterfaces = networkInterfaces; + +// -------------------------- +// NET - Speed + +function calcNetworkSpeed(iface, rx_bytes, tx_bytes, operstate, rx_dropped, rx_errors, tx_dropped, tx_errors) { + let result = { + iface, + operstate, + rx_bytes, + rx_dropped, + rx_errors, + tx_bytes, + tx_dropped, + tx_errors, + rx_sec: null, + tx_sec: null, + ms: 0 + }; + + if (_network[iface] && _network[iface].ms) { + result.ms = Date.now() - _network[iface].ms; + result.rx_sec = (rx_bytes - _network[iface].rx_bytes) >= 0 ? (rx_bytes - _network[iface].rx_bytes) / (result.ms / 1000) : 0; + result.tx_sec = (tx_bytes - _network[iface].tx_bytes) >= 0 ? (tx_bytes - _network[iface].tx_bytes) / (result.ms / 1000) : 0; + _network[iface].rx_bytes = rx_bytes; + _network[iface].tx_bytes = tx_bytes; + _network[iface].rx_sec = result.rx_sec; + _network[iface].tx_sec = result.tx_sec; + _network[iface].ms = Date.now(); + _network[iface].last_ms = result.ms; + _network[iface].operstate = operstate; + } else { + if (!_network[iface]) { _network[iface] = {}; } + _network[iface].rx_bytes = rx_bytes; + _network[iface].tx_bytes = tx_bytes; + _network[iface].rx_sec = null; + _network[iface].tx_sec = null; + _network[iface].ms = Date.now(); + _network[iface].last_ms = 0; + _network[iface].operstate = operstate; + } + return result; +} + +function networkStats(ifaces, callback) { + + let ifacesArray = []; + + return new Promise((resolve) => { + process.nextTick(() => { + + // fallback - if only callback is given + if (util.isFunction(ifaces) && !callback) { + callback = ifaces; + ifacesArray = [getDefaultNetworkInterface()]; + } else { + if (typeof ifaces !== 'string' && ifaces !== undefined) { + if (callback) { callback([]); } + return resolve([]); + } + ifaces = ifaces || getDefaultNetworkInterface(); + + ifaces.__proto__.toLowerCase = util.stringToLower; + ifaces.__proto__.replace = util.stringReplace; + ifaces.__proto__.trim = util.stringTrim; + + ifaces = ifaces.trim().toLowerCase().replace(/,+/g, '|'); + ifacesArray = ifaces.split('|'); + } + + const result = []; + + const workload = []; + if (ifacesArray.length && ifacesArray[0].trim() === '*') { + ifacesArray = []; + networkInterfaces(false).then(allIFaces => { + for (let iface of allIFaces) { + ifacesArray.push(iface.iface); + } + networkStats(ifacesArray.join(',')).then(result => { + if (callback) { callback(result); } + resolve(result); + }); + }); + } else { + for (let iface of ifacesArray) { + workload.push(networkStatsSingle(iface.trim())); + } + if (workload.length) { + Promise.all( + workload + ).then(data => { + if (callback) { callback(data); } + resolve(data); + }); + } else { + if (callback) { callback(result); } + resolve(result); + } + } + }); + }); +} + +function networkStatsSingle(iface) { + + function parseLinesWindowsPerfData(sections) { + let perfData = []; + for (let i in sections) { + if ({}.hasOwnProperty.call(sections, i)) { + if (sections[i].trim() !== '') { + let lines = sections[i].trim().split('\r\n'); + perfData.push({ + name: util.getValue(lines, 'Name', ':').replace(/[()[\] ]+/g, '').replace('#', '_').toLowerCase(), + rx_bytes: parseInt(util.getValue(lines, 'BytesReceivedPersec', ':'), 10), + rx_errors: parseInt(util.getValue(lines, 'PacketsReceivedErrors', ':'), 10), + rx_dropped: parseInt(util.getValue(lines, 'PacketsReceivedDiscarded', ':'), 10), + tx_bytes: parseInt(util.getValue(lines, 'BytesSentPersec', ':'), 10), + tx_errors: parseInt(util.getValue(lines, 'PacketsOutboundErrors', ':'), 10), + tx_dropped: parseInt(util.getValue(lines, 'PacketsOutboundDiscarded', ':'), 10) + }); + } + } + } + return perfData; + } + + return new Promise((resolve) => { + process.nextTick(() => { + let ifaceSanitized = ''; + const s = util.isPrototypePolluted() ? '---' : util.sanitizeShellString(iface); + for (let i = 0; i <= util.mathMin(s.length, 2000); i++) { + if (!(s[i] === undefined)) { + ifaceSanitized = ifaceSanitized + s[i]; + } + } + + let result = { + iface: ifaceSanitized, + operstate: 'unknown', + rx_bytes: 0, + rx_dropped: 0, + rx_errors: 0, + tx_bytes: 0, + tx_dropped: 0, + tx_errors: 0, + rx_sec: null, + tx_sec: null, + ms: 0 + }; + + let operstate = 'unknown'; + let rx_bytes = 0; + let tx_bytes = 0; + let rx_dropped = 0; + let rx_errors = 0; + let tx_dropped = 0; + let tx_errors = 0; + + let cmd, lines, stats; + if (!_network[ifaceSanitized] || (_network[ifaceSanitized] && !_network[ifaceSanitized].ms) || (_network[ifaceSanitized] && _network[ifaceSanitized].ms && Date.now() - _network[ifaceSanitized].ms >= 500)) { + if (_linux) { + if (fs.existsSync('/sys/class/net/' + ifaceSanitized)) { + cmd = + 'cat /sys/class/net/' + ifaceSanitized + '/operstate; ' + + 'cat /sys/class/net/' + ifaceSanitized + '/statistics/rx_bytes; ' + + 'cat /sys/class/net/' + ifaceSanitized + '/statistics/tx_bytes; ' + + 'cat /sys/class/net/' + ifaceSanitized + '/statistics/rx_dropped; ' + + 'cat /sys/class/net/' + ifaceSanitized + '/statistics/rx_errors; ' + + 'cat /sys/class/net/' + ifaceSanitized + '/statistics/tx_dropped; ' + + 'cat /sys/class/net/' + ifaceSanitized + '/statistics/tx_errors; '; + exec(cmd, function (error, stdout) { + if (!error) { + lines = stdout.toString().split('\n'); + operstate = lines[0].trim(); + rx_bytes = parseInt(lines[1], 10); + tx_bytes = parseInt(lines[2], 10); + rx_dropped = parseInt(lines[3], 10); + rx_errors = parseInt(lines[4], 10); + tx_dropped = parseInt(lines[5], 10); + tx_errors = parseInt(lines[6], 10); + + result = calcNetworkSpeed(ifaceSanitized, rx_bytes, tx_bytes, operstate, rx_dropped, rx_errors, tx_dropped, tx_errors); + + } + resolve(result); + }); + } else { + resolve(result); + } + } + if (_freebsd || _openbsd || _netbsd) { + cmd = 'netstat -ibndI ' + ifaceSanitized; // lgtm [js/shell-command-constructed-from-input] + exec(cmd, function (error, stdout) { + if (!error) { + lines = stdout.toString().split('\n'); + for (let i = 1; i < lines.length; i++) { + const line = lines[i].replace(/ +/g, ' ').split(' '); + if (line && line[0] && line[7] && line[10]) { + rx_bytes = rx_bytes + parseInt(line[7]); + if (line[6].trim() !== '-') { rx_dropped = rx_dropped + parseInt(line[6]); } + if (line[5].trim() !== '-') { rx_errors = rx_errors + parseInt(line[5]); } + tx_bytes = tx_bytes + parseInt(line[10]); + if (line[12].trim() !== '-') { tx_dropped = tx_dropped + parseInt(line[12]); } + if (line[9].trim() !== '-') { tx_errors = tx_errors + parseInt(line[9]); } + operstate = 'up'; + } + } + result = calcNetworkSpeed(ifaceSanitized, rx_bytes, tx_bytes, operstate, rx_dropped, rx_errors, tx_dropped, tx_errors); + } + resolve(result); + }); + } + if (_darwin) { + cmd = 'ifconfig ' + ifaceSanitized + ' | grep "status"'; // lgtm [js/shell-command-constructed-from-input] + exec(cmd, function (error, stdout) { + result.operstate = (stdout.toString().split(':')[1] || '').trim(); + result.operstate = (result.operstate || '').toLowerCase(); + result.operstate = (result.operstate === 'active' ? 'up' : (result.operstate === 'inactive' ? 'down' : 'unknown')); + cmd = 'netstat -bdI ' + ifaceSanitized; // lgtm [js/shell-command-constructed-from-input] + exec(cmd, function (error, stdout) { + if (!error) { + lines = stdout.toString().split('\n'); + // if there is less than 2 lines, no information for this interface was found + if (lines.length > 1 && lines[1].trim() !== '') { + // skip header line + // use the second line because it is tied to the NIC instead of the ipv4 or ipv6 address + stats = lines[1].replace(/ +/g, ' ').split(' '); + const offset = stats.length > 11 ? 1 : 0; + rx_bytes = parseInt(stats[offset + 5]); + rx_dropped = parseInt(stats[offset + 10]); + rx_errors = parseInt(stats[offset + 4]); + tx_bytes = parseInt(stats[offset + 8]); + tx_dropped = parseInt(stats[offset + 10]); + tx_errors = parseInt(stats[offset + 7]); + result = calcNetworkSpeed(ifaceSanitized, rx_bytes, tx_bytes, result.operstate, rx_dropped, rx_errors, tx_dropped, tx_errors); + } + } + resolve(result); + }); + }); + } + if (_windows) { + let perfData = []; + let ifaceName = ifaceSanitized; + + // Performance Data + util.powerShell('Get-WmiObject Win32_PerfRawData_Tcpip_NetworkInterface | select Name,BytesReceivedPersec,PacketsReceivedErrors,PacketsReceivedDiscarded,BytesSentPersec,PacketsOutboundErrors,PacketsOutboundDiscarded | fl').then((stdout, error) => { + if (!error) { + const psections = stdout.toString().split(/\n\s*\n/); + perfData = parseLinesWindowsPerfData(psections); + } + + // Network Interfaces + networkInterfaces(false).then(interfaces => { + // get bytes sent, received from perfData by name + rx_bytes = 0; + tx_bytes = 0; + perfData.forEach(detail => { + interfaces.forEach(det => { + if ((det.iface.toLowerCase() === ifaceSanitized.toLowerCase() || + det.mac.toLowerCase() === ifaceSanitized.toLowerCase() || + det.ip4.toLowerCase() === ifaceSanitized.toLowerCase() || + det.ip6.toLowerCase() === ifaceSanitized.toLowerCase() || + det.ifaceName.replace(/[()[\] ]+/g, '').replace('#', '_').toLowerCase() === ifaceSanitized.replace(/[()[\] ]+/g, '').replace('#', '_').toLowerCase()) && + (det.ifaceName.replace(/[()[\] ]+/g, '').replace('#', '_').toLowerCase() === detail.name)) { + ifaceName = det.iface; + rx_bytes = detail.rx_bytes; + rx_dropped = detail.rx_dropped; + rx_errors = detail.rx_errors; + tx_bytes = detail.tx_bytes; + tx_dropped = detail.tx_dropped; + tx_errors = detail.tx_errors; + operstate = det.operstate; + } + }); + }); + if (rx_bytes && tx_bytes) { + result = calcNetworkSpeed(ifaceName, parseInt(rx_bytes), parseInt(tx_bytes), operstate, rx_dropped, rx_errors, tx_dropped, tx_errors); + } + resolve(result); + }); + }); + } + } else { + result.rx_bytes = _network[ifaceSanitized].rx_bytes; + result.tx_bytes = _network[ifaceSanitized].tx_bytes; + result.rx_sec = _network[ifaceSanitized].rx_sec; + result.tx_sec = _network[ifaceSanitized].tx_sec; + result.ms = _network[ifaceSanitized].last_ms; + result.operstate = _network[ifaceSanitized].operstate; + resolve(result); + } + }); + }); +} + +exports.networkStats = networkStats; + +// -------------------------- +// NET - connections (sockets) + +function networkConnections(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + let result = []; + if (_linux || _freebsd || _openbsd || _netbsd) { + let cmd = 'export LC_ALL=C; netstat -tunap | grep "ESTABLISHED\\|SYN_SENT\\|SYN_RECV\\|FIN_WAIT1\\|FIN_WAIT2\\|TIME_WAIT\\|CLOSE\\|CLOSE_WAIT\\|LAST_ACK\\|LISTEN\\|CLOSING\\|UNKNOWN"; unset LC_ALL'; + if (_freebsd || _openbsd || _netbsd) { cmd = 'export LC_ALL=C; netstat -na | grep "ESTABLISHED\\|SYN_SENT\\|SYN_RECV\\|FIN_WAIT1\\|FIN_WAIT2\\|TIME_WAIT\\|CLOSE\\|CLOSE_WAIT\\|LAST_ACK\\|LISTEN\\|CLOSING\\|UNKNOWN"; unset LC_ALL'; } + exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) { + let lines = stdout.toString().split('\n'); + if (!error && (lines.length > 1 || lines[0] != '')) { + lines.forEach(function (line) { + line = line.replace(/ +/g, ' ').split(' '); + if (line.length >= 7) { + let localip = line[3]; + let localport = ''; + let localaddress = line[3].split(':'); + if (localaddress.length > 1) { + localport = localaddress[localaddress.length - 1]; + localaddress.pop(); + localip = localaddress.join(':'); + } + let peerip = line[4]; + let peerport = ''; + let peeraddress = line[4].split(':'); + if (peeraddress.length > 1) { + peerport = peeraddress[peeraddress.length - 1]; + peeraddress.pop(); + peerip = peeraddress.join(':'); + } + let connstate = line[5]; + // if (connstate === 'VERBUNDEN') connstate = 'ESTABLISHED'; + let proc = line[6].split('/'); + + if (connstate) { + result.push({ + protocol: line[0], + localAddress: localip, + localPort: localport, + peerAddress: peerip, + peerPort: peerport, + state: connstate, + pid: proc[0] && proc[0] !== '-' ? parseInt(proc[0], 10) : null, + process: proc[1] ? proc[1].split(' ')[0] : '' + }); + } + } + }); + if (callback) { + callback(result); + } + resolve(result); + } else { + cmd = 'ss -tunap | grep "ESTAB\\|SYN-SENT\\|SYN-RECV\\|FIN-WAIT1\\|FIN-WAIT2\\|TIME-WAIT\\|CLOSE\\|CLOSE-WAIT\\|LAST-ACK\\|LISTEN\\|CLOSING"'; + exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) { + + if (!error) { + let lines = stdout.toString().split('\n'); + lines.forEach(function (line) { + line = line.replace(/ +/g, ' ').split(' '); + if (line.length >= 6) { + let localip = line[4]; + let localport = ''; + let localaddress = line[4].split(':'); + if (localaddress.length > 1) { + localport = localaddress[localaddress.length - 1]; + localaddress.pop(); + localip = localaddress.join(':'); + } + let peerip = line[5]; + let peerport = ''; + let peeraddress = line[5].split(':'); + if (peeraddress.length > 1) { + peerport = peeraddress[peeraddress.length - 1]; + peeraddress.pop(); + peerip = peeraddress.join(':'); + } + let connstate = line[1]; + if (connstate === 'ESTAB') { connstate = 'ESTABLISHED'; } + if (connstate === 'TIME-WAIT') { connstate = 'TIME_WAIT'; } + let pid = null; + let process = ''; + if (line.length >= 7 && line[6].indexOf('users:') > -1) { + let proc = line[6].replace('users:(("', '').replace(/"/g, '').split(','); + if (proc.length > 2) { + process = proc[0].split(' ')[0]; + pid = parseInt(proc[1], 10); + } + } + if (connstate) { + result.push({ + protocol: line[0], + localAddress: localip, + localPort: localport, + peerAddress: peerip, + peerPort: peerport, + state: connstate, + pid, + process + }); + } + } + }); + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + }); + } + if (_darwin) { + let cmd = 'netstat -natv | grep "ESTABLISHED\\|SYN_SENT\\|SYN_RECV\\|FIN_WAIT1\\|FIN_WAIT2\\|TIME_WAIT\\|CLOSE\\|CLOSE_WAIT\\|LAST_ACK\\|LISTEN\\|CLOSING\\|UNKNOWN"'; + exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) { + if (!error) { + + let lines = stdout.toString().split('\n'); + + lines.forEach(function (line) { + line = line.replace(/ +/g, ' ').split(' '); + if (line.length >= 8) { + let localip = line[3]; + let localport = ''; + let localaddress = line[3].split('.'); + if (localaddress.length > 1) { + localport = localaddress[localaddress.length - 1]; + localaddress.pop(); + localip = localaddress.join('.'); + } + let peerip = line[4]; + let peerport = ''; + let peeraddress = line[4].split('.'); + if (peeraddress.length > 1) { + peerport = peeraddress[peeraddress.length - 1]; + peeraddress.pop(); + peerip = peeraddress.join('.'); + } + let connstate = line[5]; + let pid = parseInt(line[8], 10); + if (connstate) { + result.push({ + protocol: line[0], + localAddress: localip, + localPort: localport, + peerAddress: peerip, + peerPort: peerport, + state: connstate, + pid: pid, + process: '' + }); + } + } + }); + if (callback) { + callback(result); + } + resolve(result); + } + }); + } + if (_windows) { + let cmd = 'netstat -nao'; + try { + exec(cmd, util.execOptsWin, function (error, stdout) { + if (!error) { + + let lines = stdout.toString().split('\r\n'); + + lines.forEach(function (line) { + line = line.trim().replace(/ +/g, ' ').split(' '); + if (line.length >= 4) { + let localip = line[1]; + let localport = ''; + let localaddress = line[1].split(':'); + if (localaddress.length > 1) { + localport = localaddress[localaddress.length - 1]; + localaddress.pop(); + localip = localaddress.join(':'); + } + let peerip = line[2]; + let peerport = ''; + let peeraddress = line[2].split(':'); + if (peeraddress.length > 1) { + peerport = peeraddress[peeraddress.length - 1]; + peeraddress.pop(); + peerip = peeraddress.join(':'); + } + let pid = util.toInt(line[4]); + let connstate = line[3]; + if (connstate === 'HERGESTELLT') { connstate = 'ESTABLISHED'; } + if (connstate.startsWith('ABH')) { connstate = 'LISTEN'; } + if (connstate === 'SCHLIESSEN_WARTEN') { connstate = 'CLOSE_WAIT'; } + if (connstate === 'WARTEND') { connstate = 'TIME_WAIT'; } + if (connstate === 'SYN_GESENDET') { connstate = 'SYN_SENT'; } + + if (connstate === 'LISTENING') { connstate = 'LISTEN'; } + if (connstate === 'SYN_RECEIVED') { connstate = 'SYN_RECV'; } + if (connstate === 'FIN_WAIT_1') { connstate = 'FIN_WAIT1'; } + if (connstate === 'FIN_WAIT_2') { connstate = 'FIN_WAIT2'; } + if (connstate) { + result.push({ + protocol: line[0].toLowerCase(), + localAddress: localip, + localPort: localport, + peerAddress: peerip, + peerPort: peerport, + state: connstate, + pid, + process: '' + }); + } + } + }); + if (callback) { + callback(result); + } + resolve(result); + } + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + }); + }); +} + +exports.networkConnections = networkConnections; + +function networkGatewayDefault(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + let result = ''; + if (_linux || _freebsd || _openbsd || _netbsd) { + let cmd = 'ip route get 1'; + try { + exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + const line = lines && lines[0] ? lines[0] : ''; + let parts = line.split(' via '); + if (parts && parts[1]) { + parts = parts[1].split(' '); + result = parts[0]; + } + if (callback) { + callback(result); + } + resolve(result); + } else { + if (callback) { + callback(result); + } + resolve(result); + } + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + if (_darwin) { + let cmd = 'route -n get default'; + try { + exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) { + if (!error) { + const lines = stdout.toString().split('\n').map(line => line.trim()); + result = util.getValue(lines, 'gateway'); + } + if (!result) { + cmd = 'netstat -rn | awk \'/default/ {print $2}\''; + exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) { + const lines = stdout.toString().split('\n').map(line => line.trim()); + result = lines.find(line => (/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(line))); + if (callback) { + callback(result); + } + resolve(result); + }); + } else { + if (callback) { + callback(result); + } + resolve(result); + } + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + if (_windows) { + try { + exec('netstat -r', util.execOptsWin, function (error, stdout) { + const lines = stdout.toString().split(os.EOL); + lines.forEach(line => { + line = line.replace(/\s+/g, ' ').trim(); + if (line.indexOf('0.0.0.0 0.0.0.0') > -1 && !(/[a-zA-Z]/.test(line))) { + const parts = line.split(' '); + if (parts.length >= 5 && (parts[parts.length - 3]).indexOf('.') > -1) { + result = parts[parts.length - 3]; + } + } + }); + if (!result) { + util.powerShell('Get-CimInstance -ClassName Win32_IP4RouteTable | Where-Object { $_.Destination -eq \'0.0.0.0\' -and $_.Mask -eq \'0.0.0.0\' }') + .then(data => { + let lines = data.toString().split('\r\n'); + if (lines.length > 1 && !result) { + result = util.getValue(lines, 'NextHop'); + if (callback) { + callback(result); + } + resolve(result); + // } else { + // exec('ipconfig', util.execOptsWin, function (error, stdout) { + // let lines = stdout.toString().split('\r\n'); + // lines.forEach(function (line) { + // line = line.trim().replace(/\. /g, ''); + // line = line.trim().replace(/ +/g, ''); + // const parts = line.split(':'); + // if ((parts[0].toLowerCase().startsWith('standardgate') || parts[0].toLowerCase().indexOf('gateway') > -1 || parts[0].toLowerCase().indexOf('enlace') > -1) && parts[1]) { + // result = parts[1]; + // } + // }); + // if (callback) { callback(result); } + // resolve(result); + // }); + } + }); + } else { + if (callback) { + callback(result); + } + resolve(result); + } + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + }); + }); +} + +exports.networkGatewayDefault = networkGatewayDefault; diff --git a/node_modules/systeminformation/lib/osinfo.js b/node_modules/systeminformation/lib/osinfo.js new file mode 100644 index 0000000..ff66c3c --- /dev/null +++ b/node_modules/systeminformation/lib/osinfo.js @@ -0,0 +1,1161 @@ +'use strict'; +// @ts-check +// ================================================================================== +// osinfo.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 3. Operating System +// ---------------------------------------------------------------------------------- + +const os = require('os'); +const fs = require('fs'); +const util = require('./util'); +const exec = require('child_process').exec; +const execSync = require('child_process').execSync; +// const execPromise = util.promisify(require('child_process').exec); + +let _platform = process.platform; + +const _linux = (_platform === 'linux' || _platform === 'android'); +const _darwin = (_platform === 'darwin'); +const _windows = (_platform === 'win32'); +const _freebsd = (_platform === 'freebsd'); +const _openbsd = (_platform === 'openbsd'); +const _netbsd = (_platform === 'netbsd'); +const _sunos = (_platform === 'sunos'); + +// -------------------------- +// Get current time and OS uptime + +function time() { + let t = new Date().toString().split(' '); + return { + current: Date.now(), + uptime: os.uptime(), + timezone: (t.length >= 7) ? t[5] : '', + timezoneName: Intl ? Intl.DateTimeFormat().resolvedOptions().timeZone : (t.length >= 7) ? t.slice(6).join(' ').replace(/\(/g, '').replace(/\)/g, '') : '' + }; +} + +exports.time = time; + +// -------------------------- +// Get logo filename of OS distribution + +function getLogoFile(distro) { + distro = distro || ''; + distro = distro.toLowerCase(); + let result = _platform; + if (_windows) { + result = 'windows'; + } + else if (distro.indexOf('mac os') !== -1) { + result = 'apple'; + } + else if (distro.indexOf('arch') !== -1) { + result = 'arch'; + } + else if (distro.indexOf('centos') !== -1) { + result = 'centos'; + } + else if (distro.indexOf('coreos') !== -1) { + result = 'coreos'; + } + else if (distro.indexOf('debian') !== -1) { + result = 'debian'; + } + else if (distro.indexOf('deepin') !== -1) { + result = 'deepin'; + } + else if (distro.indexOf('elementary') !== -1) { + result = 'elementary'; + } + else if (distro.indexOf('fedora') !== -1) { + result = 'fedora'; + } + else if (distro.indexOf('gentoo') !== -1) { + result = 'gentoo'; + } + else if (distro.indexOf('mageia') !== -1) { + result = 'mageia'; + } + else if (distro.indexOf('mandriva') !== -1) { + result = 'mandriva'; + } + else if (distro.indexOf('manjaro') !== -1) { + result = 'manjaro'; + } + else if (distro.indexOf('mint') !== -1) { + result = 'mint'; + } + else if (distro.indexOf('mx') !== -1) { + result = 'mx'; + } + else if (distro.indexOf('openbsd') !== -1) { + result = 'openbsd'; + } + else if (distro.indexOf('freebsd') !== -1) { + result = 'freebsd'; + } + else if (distro.indexOf('opensuse') !== -1) { + result = 'opensuse'; + } + else if (distro.indexOf('pclinuxos') !== -1) { + result = 'pclinuxos'; + } + else if (distro.indexOf('puppy') !== -1) { + result = 'puppy'; + } + else if (distro.indexOf('raspbian') !== -1) { + result = 'raspbian'; + } + else if (distro.indexOf('reactos') !== -1) { + result = 'reactos'; + } + else if (distro.indexOf('redhat') !== -1) { + result = 'redhat'; + } + else if (distro.indexOf('slackware') !== -1) { + result = 'slackware'; + } + else if (distro.indexOf('sugar') !== -1) { + result = 'sugar'; + } + else if (distro.indexOf('steam') !== -1) { + result = 'steam'; + } + else if (distro.indexOf('suse') !== -1) { + result = 'suse'; + } + else if (distro.indexOf('mate') !== -1) { + result = 'ubuntu-mate'; + } + else if (distro.indexOf('lubuntu') !== -1) { + result = 'lubuntu'; + } + else if (distro.indexOf('xubuntu') !== -1) { + result = 'xubuntu'; + } + else if (distro.indexOf('ubuntu') !== -1) { + result = 'ubuntu'; + } + else if (distro.indexOf('solaris') !== -1) { + result = 'solaris'; + } + else if (distro.indexOf('tails') !== -1) { + result = 'tails'; + } + else if (distro.indexOf('feren') !== -1) { + result = 'ferenos'; + } + else if (distro.indexOf('robolinux') !== -1) { + result = 'robolinux'; + } else if (_linux && distro) { + result = distro.toLowerCase().trim().replace(/\s+/g, '-'); + } + return result; +} + +// -------------------------- +// FQDN + +function getFQDN() { + let fqdn = os.hostname; + if (_linux || _darwin) { + try { + const stdout = execSync('hostname -f'); + fqdn = stdout.toString().split(os.EOL)[0]; + } catch (e) { + util.noop(); + } + } + if (_freebsd || _openbsd || _netbsd) { + try { + const stdout = execSync('hostname'); + fqdn = stdout.toString().split(os.EOL)[0]; + } catch (e) { + util.noop(); + } + } + if (_windows) { + try { + const stdout = execSync('echo %COMPUTERNAME%.%USERDNSDOMAIN%', util.execOptsWin); + fqdn = stdout.toString().replace('.%USERDNSDOMAIN%', '').split(os.EOL)[0]; + } catch (e) { + util.noop(); + } + } + return fqdn; +} + +// -------------------------- +// OS Information + +function osInfo(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + let result = { + + platform: (_platform === 'win32' ? 'Windows' : _platform), + distro: 'unknown', + release: 'unknown', + codename: '', + kernel: os.release(), + arch: os.arch(), + hostname: os.hostname(), + fqdn: getFQDN(), + codepage: '', + logofile: '', + serial: '', + build: '', + servicepack: '', + uefi: false + }; + + if (_linux) { + + exec('cat /etc/*-release; cat /usr/lib/os-release; cat /etc/openwrt_release', function (error, stdout) { + //if (!error) { + /** + * @namespace + * @property {string} DISTRIB_ID + * @property {string} NAME + * @property {string} DISTRIB_RELEASE + * @property {string} VERSION_ID + * @property {string} DISTRIB_CODENAME + */ + let release = {}; + let lines = stdout.toString().split('\n'); + lines.forEach(function (line) { + if (line.indexOf('=') !== -1) { + release[line.split('=')[0].trim().toUpperCase()] = line.split('=')[1].trim(); + } + }); + let releaseVersion = (release.VERSION || '').replace(/"/g, ''); + let codename = (release.DISTRIB_CODENAME || release.VERSION_CODENAME || '').replace(/"/g, ''); + if (releaseVersion.indexOf('(') >= 0) { + codename = releaseVersion.split('(')[1].replace(/[()]/g, '').trim(); + releaseVersion = releaseVersion.split('(')[0].trim(); + } + result.distro = (release.DISTRIB_ID || release.NAME || 'unknown').replace(/"/g, ''); + result.logofile = getLogoFile(result.distro); + result.release = (releaseVersion || release.DISTRIB_RELEASE || release.VERSION_ID || 'unknown').replace(/"/g, ''); + result.codename = codename; + result.codepage = util.getCodepage(); + result.build = (release.BUILD_ID || '').replace(/"/g, '').trim(); + isUefiLinux().then(uefi => { + result.uefi = uefi; + uuid().then(data => { + result.serial = data.os; + if (callback) { + callback(result); + } + resolve(result); + }); + }); + //} + }); + } + if (_freebsd || _openbsd || _netbsd) { + + exec('sysctl kern.ostype kern.osrelease kern.osrevision kern.hostuuid machdep.bootmethod', function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + result.distro = util.getValue(lines, 'kern.ostype'); + result.logofile = getLogoFile(result.distro); + result.release = util.getValue(lines, 'kern.osrelease').split('-')[0]; + result.serial = util.getValue(lines, 'kern.uuid'); + result.codename = ''; + result.codepage = util.getCodepage(); + result.uefi = util.getValue(lines, 'machdep.bootmethod').toLowerCase().indexOf('uefi') >= 0; + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + if (_darwin) { + exec('sw_vers; sysctl kern.ostype kern.osrelease kern.osrevision kern.uuid', function (error, stdout) { + let lines = stdout.toString().split('\n'); + result.serial = util.getValue(lines, 'kern.uuid'); + result.distro = util.getValue(lines, 'ProductName'); + result.release = util.getValue(lines, 'ProductVersion'); + result.build = util.getValue(lines, 'BuildVersion'); + result.logofile = getLogoFile(result.distro); + result.codename = 'macOS'; + result.codename = (result.release.indexOf('10.4') > -1 ? 'Mac OS X Tiger' : result.codename); + result.codename = (result.release.indexOf('10.4') > -1 ? 'Mac OS X Tiger' : result.codename); + result.codename = (result.release.indexOf('10.4') > -1 ? 'Mac OS X Tiger' : result.codename); + result.codename = (result.release.indexOf('10.5') > -1 ? 'Mac OS X Leopard' : result.codename); + result.codename = (result.release.indexOf('10.6') > -1 ? 'Mac OS X Snow Leopard' : result.codename); + result.codename = (result.release.indexOf('10.7') > -1 ? 'Mac OS X Lion' : result.codename); + result.codename = (result.release.indexOf('10.8') > -1 ? 'OS X Mountain Lion' : result.codename); + result.codename = (result.release.indexOf('10.9') > -1 ? 'OS X Mavericks' : result.codename); + result.codename = (result.release.indexOf('10.10') > -1 ? 'OS X Yosemite' : result.codename); + result.codename = (result.release.indexOf('10.11') > -1 ? 'OS X El Capitan' : result.codename); + result.codename = (result.release.indexOf('10.12') > -1 ? 'macOS Sierra' : result.codename); + result.codename = (result.release.indexOf('10.13') > -1 ? 'macOS High Sierra' : result.codename); + result.codename = (result.release.indexOf('10.14') > -1 ? 'macOS Mojave' : result.codename); + result.codename = (result.release.indexOf('10.15') > -1 ? 'macOS Catalina' : result.codename); + result.codename = (result.release.startsWith('11.') ? 'macOS Big Sur' : result.codename); + result.codename = (result.release.startsWith('12.') ? 'macOS Monterey' : result.codename); + result.uefi = true; + result.codepage = util.getCodepage(); + if (callback) { + callback(result); + } + resolve(result); + }); + } + if (_sunos) { + result.release = result.kernel; + exec('uname -o', function (error, stdout) { + let lines = stdout.toString().split('\n'); + result.distro = lines[0]; + result.logofile = getLogoFile(result.distro); + if (callback) { callback(result); } + resolve(result); + }); + } + if (_windows) { + result.logofile = getLogoFile(); + result.release = result.kernel; + try { + const workload = []; + workload.push(util.powerShell('Get-WmiObject Win32_OperatingSystem | select Caption,SerialNumber,BuildNumber,ServicePackMajorVersion,ServicePackMinorVersion | fl')); + // workload.push(execPromise('systeminfo', util.execOptsWin)); + // workload.push(util.powerShell('Get-ComputerInfo -property "HyperV*"')); + workload.push(util.powerShell('(Get-CimInstance Win32_ComputerSystem).HypervisorPresent')); + workload.push(util.powerShell('Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SystemInformation]::TerminalServerSession')); + util.promiseAll( + workload + ).then(data => { + let lines = data.results[0] ? data.results[0].toString().split('\r\n') : ['']; + result.distro = util.getValue(lines, 'Caption', ':').trim(); + result.serial = util.getValue(lines, 'SerialNumber', ':').trim(); + result.build = util.getValue(lines, 'BuildNumber', ':').trim(); + result.servicepack = util.getValue(lines, 'ServicePackMajorVersion', ':').trim() + '.' + util.getValue(lines, 'ServicePackMinorVersion', ':').trim(); + result.codepage = util.getCodepage(); + // const systeminfo = data.results[1] ? data.results[1].toString() : ''; + // result.hypervisor = (systeminfo.indexOf('hypervisor has been detected') !== -1) || (systeminfo.indexOf('ein Hypervisor erkannt') !== -1) || (systeminfo.indexOf('Un hyperviseur a ') !== -1); + // const hyperv = data.results[1] ? data.results[1].toString().split('\r\n') : []; + // result.hypervisor = (util.getValue(hyperv, 'HyperVisorPresent').toLowerCase() === 'true'); + const hyperv = data.results[1] ? data.results[1].toString().toLowerCase() : ''; + result.hypervisor = hyperv.indexOf('true') !== -1; + const term = data.results[2] ? data.results[2].toString() : ''; + result.remoteSession = (term.toString().toLowerCase().indexOf('true') >= 0); + isUefiWindows().then(uefi => { + result.uefi = uefi; + if (callback) { + callback(result); + } + resolve(result); + }); + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + }); + }); +} + +exports.osInfo = osInfo; + +function isUefiLinux() { + return new Promise((resolve) => { + process.nextTick(() => { + fs.stat('/sys/firmware/efi', function (err) { + if (!err) { + return resolve(true); + } else { + exec('dmesg | grep -E "EFI v"', function (error, stdout) { + if (!error) { + const lines = stdout.toString().split('\n'); + return resolve(lines.length > 0); + } + return resolve(false); + }); + } + }); + }); + }); +} + +function isUefiWindows() { + return new Promise((resolve) => { + process.nextTick(() => { + try { + exec('findstr /C:"Detected boot environment" "%windir%\\Panther\\setupact.log"', util.execOptsWin, function (error, stdout) { + if (!error) { + const line = stdout.toString().split('\n\r')[0]; + return resolve(line.toLowerCase().indexOf('efi') >= 0); + } else { + exec('echo %firmware_type%', util.execOptsWin, function (error, stdout) { + if (!error) { + const line = stdout.toString() || ''; + return resolve(line.toLowerCase().indexOf('efi') >= 0); + } else { + return resolve(false); + } + }); + } + }); + } catch (e) { + return resolve(false); + } + }); + }); +} + +function versions(apps, callback) { + let versionObject = { + kernel: os.release(), + openssl: '', + systemOpenssl: '', + systemOpensslLib: '', + node: process.versions.node, + v8: process.versions.v8, + npm: '', + yarn: '', + pm2: '', + gulp: '', + grunt: '', + git: '', + tsc: '', + mysql: '', + redis: '', + mongodb: '', + apache: '', + nginx: '', + php: '', + docker: '', + postfix: '', + postgresql: '', + perl: '', + python: '', + python3: '', + pip: '', + pip3: '', + java: '', + gcc: '', + virtualbox: '', + bash: '', + zsh: '', + fish: '', + powershell: '', + dotnet: '' + }; + + function checkVersionParam(apps) { + if (apps === '*') { + return { + versions: versionObject, + counter: 30 + }; + } + if (!Array.isArray(apps)) { + apps = apps.trim().toLowerCase().replace(/,+/g, '|').replace(/ /g, '|'); + apps = apps.split('|'); + const result = { + versions: {}, + counter: 0 + }; + apps.forEach(el => { + if (el) { + for (let key in versionObject) { + if ({}.hasOwnProperty.call(versionObject, key)) { + if (key.toLowerCase() === el.toLowerCase() && !{}.hasOwnProperty.call(result.versions, key)) { + result.versions[key] = versionObject[key]; + if (key === 'openssl') { + result.versions.systemOpenssl = ''; + result.versions.systemOpensslLib = ''; + } + + if (!result.versions[key]) { result.counter++; } + } + } + } + } + }); + return result; + } + } + + return new Promise((resolve) => { + process.nextTick(() => { + if (util.isFunction(apps) && !callback) { + callback = apps; + apps = '*'; + } else { + apps = apps || '*'; + if (typeof apps !== 'string') { + if (callback) { callback({}); } + return resolve({}); + } + } + const appsObj = checkVersionParam(apps); + let totalFunctions = appsObj.counter; + + let functionProcessed = (function () { + return function () { + if (--totalFunctions === 0) { + if (callback) { + callback(appsObj.versions); + } + resolve(appsObj.versions); + } + }; + })(); + + let cmd = ''; + try { + if ({}.hasOwnProperty.call(appsObj.versions, 'openssl')) { + appsObj.versions.openssl = process.versions.openssl; + exec('openssl version', function (error, stdout) { + if (!error) { + let openssl_string = stdout.toString().split('\n')[0].trim(); + let openssl = openssl_string.split(' '); + appsObj.versions.systemOpenssl = openssl.length > 0 ? openssl[1] : openssl[0]; + appsObj.versions.systemOpensslLib = openssl.length > 0 ? openssl[0] : 'openssl'; + } + functionProcessed(); + }); + } + if ({}.hasOwnProperty.call(appsObj.versions, 'npm')) { + exec('npm -v', function (error, stdout) { + if (!error) { + appsObj.versions.npm = stdout.toString().split('\n')[0]; + } + functionProcessed(); + }); + } + if ({}.hasOwnProperty.call(appsObj.versions, 'pm2')) { + cmd = 'pm2'; + if (_windows) { + cmd += '.cmd'; + } + exec(`${cmd} -v`, function (error, stdout) { + if (!error) { + let pm2 = stdout.toString().split('\n')[0].trim(); + if (!pm2.startsWith('[PM2]')) { + appsObj.versions.pm2 = pm2; + } + } + functionProcessed(); + }); + } + if ({}.hasOwnProperty.call(appsObj.versions, 'yarn')) { + exec('yarn --version', function (error, stdout) { + if (!error) { + appsObj.versions.yarn = stdout.toString().split('\n')[0]; + } + functionProcessed(); + }); + } + if ({}.hasOwnProperty.call(appsObj.versions, 'gulp')) { + cmd = 'gulp'; + if (_windows) { + cmd += '.cmd'; + } + exec(`${cmd} --version`, function (error, stdout) { + if (!error) { + const gulp = stdout.toString().split('\n')[0] || ''; + appsObj.versions.gulp = (gulp.toLowerCase().split('version')[1] || '').trim(); + } + functionProcessed(); + }); + } + if ({}.hasOwnProperty.call(appsObj.versions, 'tsc')) { + cmd = 'tsc'; + if (_windows) { + cmd += '.cmd'; + } + exec(`${cmd} --version`, function (error, stdout) { + if (!error) { + const tsc = stdout.toString().split('\n')[0] || ''; + appsObj.versions.tsc = (tsc.toLowerCase().split('version')[1] || '').trim(); + } + functionProcessed(); + }); + } + if ({}.hasOwnProperty.call(appsObj.versions, 'grunt')) { + cmd = 'grunt'; + if (_windows) { + cmd += '.cmd'; + } + exec(`${cmd} --version`, function (error, stdout) { + if (!error) { + const grunt = stdout.toString().split('\n')[0] || ''; + appsObj.versions.grunt = (grunt.toLowerCase().split('cli v')[1] || '').trim(); + } + functionProcessed(); + }); + } + if ({}.hasOwnProperty.call(appsObj.versions, 'git')) { + if (_darwin) { + const gitHomebrewExists = fs.existsSync('/usr/local/Cellar/git') || fs.existsSync('/opt/homebrew/bin/git'); + if (util.darwinXcodeExists() || gitHomebrewExists) { + exec('git --version', function (error, stdout) { + if (!error) { + let git = stdout.toString().split('\n')[0] || ''; + git = (git.toLowerCase().split('version')[1] || '').trim(); + appsObj.versions.git = (git.split(' ')[0] || '').trim(); + } + functionProcessed(); + }); + } else { + functionProcessed(); + } + } else { + exec('git --version', function (error, stdout) { + if (!error) { + let git = stdout.toString().split('\n')[0] || ''; + git = (git.toLowerCase().split('version')[1] || '').trim(); + appsObj.versions.git = (git.split(' ')[0] || '').trim(); + } + functionProcessed(); + }); + } + } + if ({}.hasOwnProperty.call(appsObj.versions, 'apache')) { + exec('apachectl -v 2>&1', function (error, stdout) { + if (!error) { + const apache = (stdout.toString().split('\n')[0] || '').split(':'); + appsObj.versions.apache = (apache.length > 1 ? apache[1].replace('Apache', '').replace('/', '').split('(')[0].trim() : ''); + } + functionProcessed(); + }); + } + if ({}.hasOwnProperty.call(appsObj.versions, 'nginx')) { + exec('nginx -v 2>&1', function (error, stdout) { + if (!error) { + const nginx = stdout.toString().split('\n')[0] || ''; + appsObj.versions.nginx = (nginx.toLowerCase().split('/')[1] || '').trim(); + } + functionProcessed(); + }); + } + if ({}.hasOwnProperty.call(appsObj.versions, 'mysql')) { + exec('mysql -V', function (error, stdout) { + if (!error) { + let mysql = stdout.toString().split('\n')[0] || ''; + mysql = mysql.toLowerCase(); + if (mysql.indexOf(',') > -1) { + mysql = (mysql.split(',')[0] || '').trim(); + const parts = mysql.split(' '); + appsObj.versions.mysql = (parts[parts.length - 1] || '').trim(); + } else { + if (mysql.indexOf(' ver ') > -1) { + mysql = mysql.split(' ver ')[1]; + appsObj.versions.mysql = mysql.split(' ')[0]; + } + } + } + functionProcessed(); + }); + } + if ({}.hasOwnProperty.call(appsObj.versions, 'php')) { + exec('php -v', function (error, stdout) { + if (!error) { + const php = stdout.toString().split('\n')[0] || ''; + let parts = php.split('('); + if (parts[0].indexOf('-')) { + parts = parts[0].split('-'); + } + appsObj.versions.php = parts[0].replace(/[^0-9.]/g, ''); + } + functionProcessed(); + }); + } + if ({}.hasOwnProperty.call(appsObj.versions, 'redis')) { + exec('redis-server --version', function (error, stdout) { + if (!error) { + const redis = stdout.toString().split('\n')[0] || ''; + const parts = redis.split(' '); + appsObj.versions.redis = util.getValue(parts, 'v', '=', true); + } + functionProcessed(); + }); + } + if ({}.hasOwnProperty.call(appsObj.versions, 'docker')) { + exec('docker --version', function (error, stdout) { + if (!error) { + const docker = stdout.toString().split('\n')[0] || ''; + const parts = docker.split(' '); + appsObj.versions.docker = parts.length > 2 && parts[2].endsWith(',') ? parts[2].slice(0, -1) : ''; + } + functionProcessed(); + }); + } + if ({}.hasOwnProperty.call(appsObj.versions, 'postfix')) { + exec('postconf -d | grep mail_version', function (error, stdout) { + if (!error) { + const postfix = stdout.toString().split('\n') || []; + appsObj.versions.postfix = util.getValue(postfix, 'mail_version', '=', true); + } + functionProcessed(); + }); + } + if ({}.hasOwnProperty.call(appsObj.versions, 'mongodb')) { + exec('mongod --version', function (error, stdout) { + if (!error) { + const mongodb = stdout.toString().split('\n')[0] || ''; + appsObj.versions.mongodb = (mongodb.toLowerCase().split(',')[0] || '').replace(/[^0-9.]/g, ''); + } + functionProcessed(); + }); + } + if ({}.hasOwnProperty.call(appsObj.versions, 'postgresql')) { + if (_linux) { + exec('locate bin/postgres', function (error, stdout) { + if (!error) { + const postgresqlBin = stdout.toString().split('\n').sort(); + if (postgresqlBin.length) { + exec(postgresqlBin[postgresqlBin.length - 1] + ' -V', function (error, stdout) { + if (!error) { + const postgresql = stdout.toString().split('\n')[0].split(' ') || []; + appsObj.versions.postgresql = postgresql.length ? postgresql[postgresql.length - 1] : ''; + } + functionProcessed(); + }); + } else { + functionProcessed(); + } + } else { + exec('psql -V', function (error, stdout) { + if (!error) { + const postgresql = stdout.toString().split('\n')[0].split(' ') || []; + appsObj.versions.postgresql = postgresql.length ? postgresql[postgresql.length - 1] : ''; + appsObj.versions.postgresql = appsObj.versions.postgresql.split('-')[0]; + } + functionProcessed(); + }); + functionProcessed(); + } + }); + } else { + if (_windows) { + util.powerShell('Get-WmiObject Win32_Service | select caption | fl').then((stdout) => { + let serviceSections = stdout.split(/\n\s*\n/); + for (let i = 0; i < serviceSections.length; i++) { + if (serviceSections[i].trim() !== '') { + let lines = serviceSections[i].trim().split('\r\n'); + let srvCaption = util.getValue(lines, 'caption', ':', true).toLowerCase(); + if (srvCaption.indexOf('postgresql') > -1) { + const parts = srvCaption.split(' server '); + if (parts.length > 1) { + appsObj.versions.postgresql = parts[1]; + } + } + } + } + functionProcessed(); + }); + } else { + exec('postgres -V', function (error, stdout) { + if (!error) { + const postgresql = stdout.toString().split('\n')[0].split(' ') || []; + appsObj.versions.postgresql = postgresql.length ? postgresql[postgresql.length - 1] : ''; + } + functionProcessed(); + }); + } + } + } + if ({}.hasOwnProperty.call(appsObj.versions, 'perl')) { + exec('perl -v', function (error, stdout) { + if (!error) { + const perl = stdout.toString().split('\n') || ''; + while (perl.length > 0 && perl[0].trim() === '') { + perl.shift(); + } + if (perl.length > 0) { + appsObj.versions.perl = perl[0].split('(').pop().split(')')[0].replace('v', ''); + } + } + functionProcessed(); + }); + } + if ({}.hasOwnProperty.call(appsObj.versions, 'python')) { + if (_darwin) { + const stdout = execSync('sw_vers'); + const lines = stdout.toString().split('\n'); + const osVersion = util.getValue(lines, 'ProductVersion', ':'); + const gitHomebrewExists1 = fs.existsSync('/usr/local/Cellar/python'); + const gitHomebrewExists2 = fs.existsSync('/opt/homebrew/bin/python'); + if ((util.darwinXcodeExists() && util.semverCompare('12.0.1', osVersion) < 0) || gitHomebrewExists1 || gitHomebrewExists2) { + const cmd = gitHomebrewExists1 ? '/usr/local/Cellar/python -V 2>&1' : (gitHomebrewExists2 ? '/opt/homebrew/bin/python -V 2>&1' : 'python -V 2>&1'); + exec(cmd, function (error, stdout) { + if (!error) { + const python = stdout.toString().split('\n')[0] || ''; + appsObj.versions.python = python.toLowerCase().replace('python', '').trim(); + } + functionProcessed(); + }); + } else { + functionProcessed(); + } + } else { + exec('python -V 2>&1', function (error, stdout) { + if (!error) { + const python = stdout.toString().split('\n')[0] || ''; + appsObj.versions.python = python.toLowerCase().replace('python', '').trim(); + } + functionProcessed(); + }); + } + } + if ({}.hasOwnProperty.call(appsObj.versions, 'python3')) { + if (_darwin) { + const gitHomebrewExists = fs.existsSync('/usr/local/Cellar/python3') || fs.existsSync('/opt/homebrew/bin/python3'); + if (util.darwinXcodeExists() || gitHomebrewExists) { + exec('python3 -V 2>&1', function (error, stdout) { + if (!error) { + const python = stdout.toString().split('\n')[0] || ''; + appsObj.versions.python3 = python.toLowerCase().replace('python', '').trim(); + } + functionProcessed(); + }); + } else { + functionProcessed(); + } + } else { + exec('python3 -V 2>&1', function (error, stdout) { + if (!error) { + const python = stdout.toString().split('\n')[0] || ''; + appsObj.versions.python3 = python.toLowerCase().replace('python', '').trim(); + } + functionProcessed(); + }); + } + } + if ({}.hasOwnProperty.call(appsObj.versions, 'pip')) { + if (_darwin) { + const gitHomebrewExists = fs.existsSync('/usr/local/Cellar/pip') || fs.existsSync('/opt/homebrew/bin/pip'); + if (util.darwinXcodeExists() || gitHomebrewExists) { + exec('pip -V 2>&1', function (error, stdout) { + if (!error) { + const pip = stdout.toString().split('\n')[0] || ''; + const parts = pip.split(' '); + appsObj.versions.pip = parts.length >= 2 ? parts[1] : ''; + } + functionProcessed(); + }); + } else { + functionProcessed(); + } + } else { + exec('pip -V 2>&1', function (error, stdout) { + if (!error) { + const pip = stdout.toString().split('\n')[0] || ''; + const parts = pip.split(' '); + appsObj.versions.pip = parts.length >= 2 ? parts[1] : ''; + } + functionProcessed(); + }); + } + } + if ({}.hasOwnProperty.call(appsObj.versions, 'pip3')) { + if (_darwin) { + const gitHomebrewExists = fs.existsSync('/usr/local/Cellar/pip3') || fs.existsSync('/opt/homebrew/bin/pip3'); + if (util.darwinXcodeExists() || gitHomebrewExists) { + exec('pip3 -V 2>&1', function (error, stdout) { + if (!error) { + const pip = stdout.toString().split('\n')[0] || ''; + const parts = pip.split(' '); + appsObj.versions.pip3 = parts.length >= 2 ? parts[1] : ''; + } + functionProcessed(); + }); + } else { + functionProcessed(); + } + } else { + exec('pip3 -V 2>&1', function (error, stdout) { + if (!error) { + const pip = stdout.toString().split('\n')[0] || ''; + const parts = pip.split(' '); + appsObj.versions.pip3 = parts.length >= 2 ? parts[1] : ''; + } + functionProcessed(); + }); + } + } + if ({}.hasOwnProperty.call(appsObj.versions, 'java')) { + if (_darwin) { + // check if any JVM is installed but avoid dialog box that Java needs to be installed + exec('/usr/libexec/java_home -V 2>&1', function (error, stdout) { + if (!error && stdout.toString().toLowerCase().indexOf('no java runtime') === -1) { + // now this can be done savely + exec('java -version 2>&1', function (error, stdout) { + if (!error) { + const java = stdout.toString().split('\n')[0] || ''; + const parts = java.split('"'); + appsObj.versions.java = parts.length === 3 ? parts[1].trim() : ''; + } + functionProcessed(); + }); + } else { + functionProcessed(); + } + }); + } else { + exec('java -version 2>&1', function (error, stdout) { + if (!error) { + const java = stdout.toString().split('\n')[0] || ''; + const parts = java.split('"'); + appsObj.versions.java = parts.length === 3 ? parts[1].trim() : ''; + } + functionProcessed(); + }); + } + } + if ({}.hasOwnProperty.call(appsObj.versions, 'gcc')) { + if ((_darwin && util.darwinXcodeExists()) || !_darwin) { + exec('gcc -dumpversion', function (error, stdout) { + if (!error) { + appsObj.versions.gcc = stdout.toString().split('\n')[0].trim() || ''; + } + if (appsObj.versions.gcc.indexOf('.') > -1) { + functionProcessed(); + } else { + exec('gcc --version', function (error, stdout) { + if (!error) { + const gcc = stdout.toString().split('\n')[0].trim(); + if (gcc.indexOf('gcc') > -1 && gcc.indexOf(')') > -1) { + const parts = gcc.split(')'); + appsObj.versions.gcc = parts[1].trim() || appsObj.versions.gcc; + } + } + functionProcessed(); + }); + } + }); + } else { + functionProcessed(); + } + } + if ({}.hasOwnProperty.call(appsObj.versions, 'virtualbox')) { + exec(util.getVboxmanage() + ' -v 2>&1', function (error, stdout) { + if (!error) { + const vbox = stdout.toString().split('\n')[0] || ''; + const parts = vbox.split('r'); + appsObj.versions.virtualbox = parts[0]; + } + functionProcessed(); + }); + } + if ({}.hasOwnProperty.call(appsObj.versions, 'bash')) { + exec('bash --version', function (error, stdout) { + if (!error) { + const line = stdout.toString().split('\n')[0]; + const parts = line.split(' version '); + if (parts.length > 1) { + appsObj.versions.bash = parts[1].split(' ')[0].split('(')[0]; + } + } + functionProcessed(); + }); + } + if ({}.hasOwnProperty.call(appsObj.versions, 'zsh')) { + exec('zsh --version', function (error, stdout) { + if (!error) { + const line = stdout.toString().split('\n')[0]; + const parts = line.split('zsh '); + if (parts.length > 1) { + appsObj.versions.zsh = parts[1].split(' ')[0]; + } + } + functionProcessed(); + }); + } + if ({}.hasOwnProperty.call(appsObj.versions, 'fish')) { + exec('fish --version', function (error, stdout) { + if (!error) { + const line = stdout.toString().split('\n')[0]; + const parts = line.split(' version '); + if (parts.length > 1) { + appsObj.versions.fish = parts[1].split(' ')[0]; + } + } + functionProcessed(); + }); + } + if ({}.hasOwnProperty.call(appsObj.versions, 'powershell')) { + if (_windows) { + util.powerShell('$PSVersionTable').then(stdout => { + const lines = stdout.toString().split('\n').map(line => line.replace(/ +/g, ' ').replace(/ +/g, ':')); + appsObj.versions.powershell = util.getValue(lines, 'psversion'); + functionProcessed(); + }); + } else { + functionProcessed(); + } + } + if ({}.hasOwnProperty.call(appsObj.versions, 'dotnet')) { + util.powerShell('gci "HKLM:\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP" -recurse | gp -name Version,Release -EA 0 | where { $_.PSChildName -match "^(?!S)\\p{L}"} | select PSChildName, Version, Release').then(stdout => { + const lines = stdout.toString().split('\r\n'); + let dotnet = ''; + lines.forEach(line => { + line = line.replace(/ +/g, ' '); + const parts = line.split(' '); + dotnet = dotnet || ((parts[0].toLowerCase().startsWith('client') && parts.length > 2 ? parts[1].trim() : (parts[0].toLowerCase().startsWith('full') && parts.length > 2 ? parts[1].trim() : ''))); + }); + appsObj.versions.dotnet = dotnet.trim(); + functionProcessed(); + }); + } + } catch (e) { + if (callback) { callback(appsObj.versions); } + resolve(appsObj.versions); + } + }); + }); +} + +exports.versions = versions; + +function shell(callback) { + return new Promise((resolve) => { + process.nextTick(() => { + if (_windows) { + resolve('cmd'); + } else { + let result = ''; + exec('echo $SHELL', function (error, stdout) { + if (!error) { + result = stdout.toString().split('\n')[0]; + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + }); + }); +} + +exports.shell = shell; + +function getUniqueMacAdresses() { + const ifaces = os.networkInterfaces(); + let macs = []; + for (let dev in ifaces) { + if ({}.hasOwnProperty.call(ifaces, dev)) { + ifaces[dev].forEach(function (details) { + if (details && details.mac && details.mac !== '00:00:00:00:00:00') { + const mac = details.mac.toLowerCase(); + if (macs.indexOf(mac) === -1) { + macs.push(mac); + } + } + }); + } + } + macs = macs.sort(function (a, b) { + if (a < b) { return -1; } + if (a > b) { return 1; } + return 0; + }); + return macs; +} + +function uuid(callback) { + return new Promise((resolve) => { + process.nextTick(() => { + + let result = { + os: '', + hardware: '', + macs: getUniqueMacAdresses() + }; + let parts; + + if (_darwin) { + exec('system_profiler SPHardwareDataType -json', function (error, stdout) { + if (!error) { + try { + const jsonObj = JSON.parse(stdout.toString()); + if (jsonObj.SPHardwareDataType && jsonObj.SPHardwareDataType.length > 0) { + const spHardware = jsonObj.SPHardwareDataType[0]; + // result.os = parts.length > 1 ? parts[1].trim().toLowerCase() : ''; + result.os = spHardware.platform_UUID.toLowerCase(); + result.hardware = spHardware.serial_number; + } + } catch (e) { + util.noop(); + } + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + if (_linux) { + const cmd = `echo -n "os: "; cat /var/lib/dbus/machine-id 2> /dev/null; echo; +echo -n "os: "; cat /etc/machine-id 2> /dev/null; echo; +echo -n "hardware: "; cat /sys/class/dmi/id/product_uuid 2> /dev/null; echo;`; + exec(cmd, function (error, stdout) { + const lines = stdout.toString().split('\n'); + result.os = util.getValue(lines, 'os').toLowerCase(); + result.hardware = util.getValue(lines, 'hardware').toLowerCase(); + if (!result.hardware) { + const lines = fs.readFileSync('/proc/cpuinfo', { encoding: 'utf8' }).toString().split('\n'); + const serial = util.getValue(lines, 'serial'); + result.hardware = serial || ''; + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + if (_freebsd || _openbsd || _netbsd) { + exec('sysctl -i kern.hostid kern.hostuuid', function (error, stdout) { + const lines = stdout.toString().split('\n'); + result.os = util.getValue(lines, 'kern.hostid', ':').toLowerCase(); + result.hardware = util.getValue(lines, 'kern.hostuuid', ':').toLowerCase(); + if (result.os.indexOf('unknown') >= 0) { result.os = ''; } + if (result.hardware.indexOf('unknown') >= 0) { result.hardware = ''; } + if (callback) { + callback(result); + } + resolve(result); + }); + } + if (_windows) { + let sysdir = '%windir%\\System32'; + if (process.arch === 'ia32' && Object.prototype.hasOwnProperty.call(process.env, 'PROCESSOR_ARCHITEW6432')) { + sysdir = '%windir%\\sysnative\\cmd.exe /c %windir%\\System32'; + } + util.powerShell('Get-WmiObject Win32_ComputerSystemProduct | select UUID | fl').then((stdout) => { + // let lines = stdout.split('\r\n').filter(line => line.trim() !== '').filter((line, idx) => idx > 0)[0].trim().split(/\s\s+/); + let lines = stdout.split('\r\n'); + result.hardware = util.getValue(lines, 'uuid', ':').toLowerCase(); + exec(`${sysdir}\\reg query "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography" /v MachineGuid`, util.execOptsWin, function (error, stdout) { + parts = stdout.toString().split('\n\r')[0].split('REG_SZ'); + result.os = parts.length > 1 ? parts[1].replace(/\r+|\n+|\s+/ig, '').toLowerCase() : ''; + if (callback) { + callback(result); + } + resolve(result); + }); + }); + } + }); + }); +} + +exports.uuid = uuid; diff --git a/node_modules/systeminformation/lib/printer.js b/node_modules/systeminformation/lib/printer.js new file mode 100644 index 0000000..1a6f319 --- /dev/null +++ b/node_modules/systeminformation/lib/printer.js @@ -0,0 +1,212 @@ +'use strict'; +// @ts-check +// ================================================================================== +// printers.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 15. printers +// ---------------------------------------------------------------------------------- + +const exec = require('child_process').exec; +// const execSync = require('child_process').execSync; +const util = require('./util'); +// const fs = require('fs'); + +let _platform = process.platform; + +const _linux = (_platform === 'linux' || _platform === 'android'); +const _darwin = (_platform === 'darwin'); +const _windows = (_platform === 'win32'); +const _freebsd = (_platform === 'freebsd'); +const _openbsd = (_platform === 'openbsd'); +const _netbsd = (_platform === 'netbsd'); +const _sunos = (_platform === 'sunos'); + +const winPrinterStatus = { + 1: 'Other', + 2: 'Unknown', + 3: 'Idle', + 4: 'Printing', + 5: 'Warmup', + 6: 'Stopped Printing', + 7: 'Offline', +}; + +function parseLinuxCupsHeader(lines) { + const result = {}; + if (lines && lines.length) { + if (lines[0].indexOf(' CUPS v') > 0) { + const parts = lines[0].split(' CUPS v'); + result.cupsVersion = parts[1]; + } + } + return result; +} + +function parseLinuxCupsPrinter(lines) { + const result = {}; + const printerId = util.getValue(lines, 'PrinterId', ' '); + result.id = printerId ? parseInt(printerId, 10) : null; + result.name = util.getValue(lines, 'Info', ' '); + result.model = lines.length > 0 && lines[0] ? lines[0].split(' ')[0] : ''; + result.uri = util.getValue(lines, 'DeviceURI', ' '); + result.uuid = util.getValue(lines, 'UUID', ' '); + result.status = util.getValue(lines, 'State', ' '); + result.local = util.getValue(lines, 'Location', ' ').toLowerCase().startsWith('local'); + result.default = null; + result.shared = util.getValue(lines, 'Shared', ' ').toLowerCase().startsWith('yes'); + + return result; +} + +function parseLinuxLpstatPrinter(lines, id) { + const result = {}; + result.id = id; + result.name = util.getValue(lines, 'Description', ':', true); + result.model = lines.length > 0 && lines[0] ? lines[0].split(' ')[0] : ''; + result.uri = null; + result.uuid = null; + result.status = lines.length > 0 && lines[0] ? (lines[0].indexOf(' idle') > 0 ? 'idle' : (lines[0].indexOf(' printing') > 0 ? 'printing' : 'unknown')) : null; + result.local = util.getValue(lines, 'Location', ':', true).toLowerCase().startsWith('local'); + result.default = null; + result.shared = util.getValue(lines, 'Shared', ' ').toLowerCase().startsWith('yes'); + + return result; +} + +function parseDarwinPrinters(printerObject, id) { + const result = {}; + const uriParts = printerObject.uri.split('/'); + result.id = id; + result.name = printerObject._name; + result.model = uriParts.length ? uriParts[uriParts.length - 1] : ''; + result.uri = printerObject.uri; + result.uuid = null; + result.status = printerObject.status; + result.local = printerObject.printserver === 'local'; + result.default = printerObject.default === 'yes'; + result.shared = printerObject.shared === 'yes'; + + return result; +} + +function parseWindowsPrinters(lines, id) { + const result = {}; + const status = parseInt(util.getValue(lines, 'PrinterStatus', ':'), 10); + + result.id = id; + result.name = util.getValue(lines, 'name', ':'); + result.model = util.getValue(lines, 'DriverName', ':'); + result.uri = null; + result.uuid = null; + result.status = winPrinterStatus[status] ? winPrinterStatus[status] : null; + result.local = util.getValue(lines, 'Local', ':').toUpperCase() === 'TRUE'; + result.default = util.getValue(lines, 'Default', ':').toUpperCase() === 'TRUE'; + result.shared = util.getValue(lines, 'Shared', ':').toUpperCase() === 'TRUE'; + + return result; +} + +function printer(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + let result = []; + if (_linux || _freebsd || _openbsd || _netbsd) { + let cmd = 'cat /etc/cups/printers.conf 2>/dev/null'; + exec(cmd, function (error, stdout) { + // printers.conf + if (!error) { + const parts = stdout.toString().split('<Printer '); + const printerHeader = parseLinuxCupsHeader(parts[0]); + for (let i = 1; i < parts.length; i++) { + const printers = parseLinuxCupsPrinter(parts[i].split('\n')); + if (printers.name) { + printers.engine = 'CUPS'; + printers.engineVersion = printerHeader.cupsVersion; + result.push(printers); + } + } + } + if (result.length === 0) { + if (_linux) { + cmd = 'export LC_ALL=C; lpstat -lp 2>/dev/null; unset LC_ALL'; + // lpstat + exec(cmd, function (error, stdout) { + const parts = ('\n' + stdout.toString()).split('\nprinter '); + for (let i = 1; i < parts.length; i++) { + const printers = parseLinuxLpstatPrinter(parts[i].split('\n'), i); + result.push(printers); + } + }); + if (callback) { + callback(result); + } + resolve(result); + } else { + if (callback) { + callback(result); + } + resolve(result); + } + } else { + if (callback) { + callback(result); + } + resolve(result); + } + }); + } + if (_darwin) { + let cmd = 'system_profiler SPPrintersDataType -json'; + exec(cmd, function (error, stdout) { + if (!error) { + try { + const outObj = JSON.parse(stdout.toString()); + if (outObj.SPPrintersDataType && outObj.SPPrintersDataType.length) { + for (let i = 0; i < outObj.SPPrintersDataType.length; i++) { + const printer = parseDarwinPrinters(outObj.SPPrintersDataType[i], i); + result.push(printer); + } + } + } catch (e) { + util.noop(); + } + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + if (_windows) { + util.powerShell('Get-WmiObject Win32_Printer | select PrinterStatus,Name,DriverName,Local,Default,Shared | fl').then((stdout, error) => { + if (!error) { + const parts = stdout.toString().split(/\n\s*\n/); + for (let i = 0; i < parts.length; i++) { + const printer = parseWindowsPrinters(parts[i].split('\n'), i); + if (printer.name || printer.model) { + result.push(parseWindowsPrinters(parts[i].split('\n'), i)); + } + } + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + if (_sunos) { + resolve(null); + } + }); + }); +} + +exports.printer = printer; diff --git a/node_modules/systeminformation/lib/processes.js b/node_modules/systeminformation/lib/processes.js new file mode 100644 index 0000000..f49d7de --- /dev/null +++ b/node_modules/systeminformation/lib/processes.js @@ -0,0 +1,1291 @@ +'use strict'; +// @ts-check +// ================================================================================== +// processes.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 10. Processes +// ---------------------------------------------------------------------------------- + +const os = require('os'); +const fs = require('fs'); +const path = require('path'); +const exec = require('child_process').exec; +const execSync = require('child_process').execSync; + +const util = require('./util'); + +let _platform = process.platform; + +const _linux = (_platform === 'linux' || _platform === 'android'); +const _darwin = (_platform === 'darwin'); +const _windows = (_platform === 'win32'); +const _freebsd = (_platform === 'freebsd'); +const _openbsd = (_platform === 'openbsd'); +const _netbsd = (_platform === 'netbsd'); +const _sunos = (_platform === 'sunos'); + +const _processes_cpu = { + all: 0, + all_utime: 0, + all_stime: 0, + list: {}, + ms: 0, + result: {} +}; +const _services_cpu = { + all: 0, + all_utime: 0, + all_stime: 0, + list: {}, + ms: 0, + result: {} +}; +const _process_cpu = { + all: 0, + all_utime: 0, + all_stime: 0, + list: {}, + ms: 0, + result: {} +}; + +const _winStatusValues = { + '0': 'unknown', + '1': 'other', + '2': 'ready', + '3': 'running', + '4': 'blocked', + '5': 'suspended blocked', + '6': 'suspended ready', + '7': 'terminated', + '8': 'stopped', + '9': 'growing', +}; + + +function parseTimeWin(time) { + time = time || ''; + if (time) { + return (time.substr(0, 4) + '-' + time.substr(4, 2) + '-' + time.substr(6, 2) + ' ' + time.substr(8, 2) + ':' + time.substr(10, 2) + ':' + time.substr(12, 2)); + } else { + return ''; + } +} + +function parseTimeUnix(time) { + let result = time; + let parts = time.replace(/ +/g, ' ').split(' '); + if (parts.length === 5) { + result = parts[4] + '-' + ('0' + ('JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC'.indexOf(parts[1].toUpperCase()) / 3 + 1)).slice(-2) + '-' + ('0' + parts[2]).slice(-2) + ' ' + parts[3]; + } + return result; +} + +function parseElapsedTime(etime) { + let current = new Date(); + current = new Date(current.getTime() - current.getTimezoneOffset() * 60000); + + const elapsed = etime.split('-'); + + const timeIndex = elapsed.length - 1; + const days = timeIndex > 0 ? parseInt(elapsed[timeIndex - 1]) : 0; + + const timeStr = elapsed[timeIndex].split(':'); + const hours = timeStr.length === 3 ? parseInt(timeStr[0] || 0) : 0; + const mins = parseInt(timeStr[timeStr.length === 3 ? 1 : 0] || 0); + const secs = parseInt(timeStr[timeStr.length === 3 ? 2 : 1] || 0); + const ms = (((((days * 24 + hours) * 60) + mins) * 60 + secs) * 1000); + + const res = new Date(current.getTime() - ms); + return res.toISOString().substring(0, 10) + ' ' + res.toISOString().substring(11, 19); +} + +// -------------------------- +// PS - services +// pass a comma separated string with services to check (mysql, apache, postgresql, ...) +// this function gives an array back, if the services are running. + +function services(srv, callback) { + + // fallback - if only callback is given + if (util.isFunction(srv) && !callback) { + callback = srv; + srv = ''; + } + + return new Promise((resolve) => { + process.nextTick(() => { + if (typeof srv !== 'string') { + if (callback) { callback([]); } + return resolve([]); + } + + if (srv) { + let srvString = ''; + srvString.__proto__.toLowerCase = util.stringToLower; + srvString.__proto__.replace = util.stringReplace; + srvString.__proto__.trim = util.stringTrim; + + const s = util.sanitizeShellString(srv); + for (let i = 0; i <= util.mathMin(s.length, 2000); i++) { + if (!(s[i] === undefined)) { + srvString = srvString + s[i]; + } + } + + srvString = srvString.trim().toLowerCase().replace(/, /g, '|').replace(/,+/g, '|'); + if (srvString === '') { + srvString = '*'; + } + if (util.isPrototypePolluted() && srvString !== '*') { + srvString = '------'; + } + let srvs = srvString.split('|'); + let result = []; + let dataSrv = []; + // let allSrv = []; + + if (_linux || _freebsd || _openbsd || _netbsd || _darwin) { + if ((_linux || _freebsd || _openbsd || _netbsd) && srvString === '*') { + try { + const tmpsrv = execSync('systemctl --type=service --no-legend 2> /dev/null').toString().split('\n'); + srvs = []; + for (const s of tmpsrv) { + const name = s.split('.service')[0]; + if (name) { + srvs.push(name); + } + } + srvString = srvs.join('|'); + } catch (d) { + try { + srvString = ''; + const tmpsrv = execSync('service --status-all 2> /dev/null').toString().split('\n'); + for (const s of tmpsrv) { + const parts = s.split(']'); + if (parts.length === 2) { + srvString += (srvString !== '' ? '|' : '') + parts[1].trim(); + // allSrv.push({ name: parts[1].trim(), running: parts[0].indexOf('+') > 0 }); + } + } + srvs = srvString.split('|'); + } catch (e) { + try { + const srvStr = execSync('ls /etc/init.d/ -m 2> /dev/null').toString().split('\n').join(''); + srvString = ''; + if (srvStr) { + const tmpsrv = srvStr.split(','); + for (const s of tmpsrv) { + const name = s.trim(); + if (name) { + srvString += (srvString !== '' ? '|' : '') + name; + // allSrv.push({ name: name, running: null }); + } + } + srvs = srvString.split('|'); + } + } catch (f) { + // allSrv = []; + srvString = ''; + srvs = []; + } + } + } + } + if ((_darwin) && srvString === '*') { // service enumeration not yet suported on mac OS + if (callback) { callback(result); } + resolve(result); + } + let args = (_darwin) ? ['-caxo', 'pcpu,pmem,pid,command'] : ['-axo', 'pcpu,pmem,pid,command']; + if (srvString !== '' && srvs.length > 0) { + util.execSafe('ps', args).then((stdout) => { + if (stdout) { + let lines = stdout.replace(/ +/g, ' ').replace(/,+/g, '.').split('\n'); + srvs.forEach(function (srv) { + let ps; + if (_darwin) { + ps = lines.filter(function (e) { + return (e.toLowerCase().indexOf(srv) !== -1); + }); + + } else { + ps = lines.filter(function (e) { + return (e.toLowerCase().indexOf(' ' + srv + ':') !== -1) || (e.toLowerCase().indexOf('/' + srv) !== -1); + }); + } + // let singleSrv = allSrv.filter(item => { return item.name === srv; }); + const pids = []; + for (const p of ps) { + const pid = p.trim().split(' ')[2]; + if (pid) { + pids.push(parseInt(pid, 10)); + } + } + result.push({ + name: srv, + // running: (allSrv.length && singleSrv.length && singleSrv[0].running !== null ? singleSrv[0].running : ps.length > 0), + running: ps.length > 0, + startmode: '', + pids: pids, + cpu: parseFloat((ps.reduce(function (pv, cv) { + return pv + parseFloat(cv.trim().split(' ')[0]); + }, 0)).toFixed(2)), + mem: parseFloat((ps.reduce(function (pv, cv) { + return pv + parseFloat(cv.trim().split(' ')[1]); + }, 0)).toFixed(2)) + }); + }); + if (_linux) { + // calc process_cpu - ps is not accurate in linux! + let cmd = 'cat /proc/stat | grep "cpu "'; + for (let i in result) { + for (let j in result[i].pids) { + cmd += (';cat /proc/' + result[i].pids[j] + '/stat'); + } + } + exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) { + let curr_processes = stdout.toString().split('\n'); + + // first line (all - /proc/stat) + let all = parseProcStat(curr_processes.shift()); + + // process + let list_new = {}; + let resultProcess = {}; + for (let i = 0; i < curr_processes.length; i++) { + resultProcess = calcProcStatLinux(curr_processes[i], all, _services_cpu); + + if (resultProcess.pid) { + let listPos = -1; + for (let i in result) { + for (let j in result[i].pids) { + if (parseInt(result[i].pids[j]) === parseInt(resultProcess.pid)) { + listPos = i; + } + } + } + if (listPos >= 0) { + result[listPos].cpu += resultProcess.cpuu + resultProcess.cpus; + } + + // save new values + list_new[resultProcess.pid] = { + cpuu: resultProcess.cpuu, + cpus: resultProcess.cpus, + utime: resultProcess.utime, + stime: resultProcess.stime, + cutime: resultProcess.cutime, + cstime: resultProcess.cstime + }; + } + } + + // store old values + _services_cpu.all = all; + // _services_cpu.list = list_new; + _services_cpu.list = Object.assign({}, list_new); + _services_cpu.ms = Date.now() - _services_cpu.ms; + // _services_cpu.result = result; + _services_cpu.result = Object.assign({}, result); + if (callback) { callback(result); } + resolve(result); + }); + } else { + if (callback) { callback(result); } + resolve(result); + } + } else { + args = ['-o', 'comm']; + util.execSafe('ps', args).then((stdout) => { + if (stdout) { + let lines = stdout.replace(/ +/g, ' ').replace(/,+/g, '.').split('\n'); + srvs.forEach(function (srv) { + let ps = lines.filter(function (e) { + return e.indexOf(srv) !== -1; + }); + result.push({ + name: srv, + running: ps.length > 0, + startmode: '', + cpu: 0, + mem: 0 + }); + }); + if (callback) { callback(result); } + resolve(result); + } else { + srvs.forEach(function (srv) { + result.push({ + name: srv, + running: false, + startmode: '', + cpu: 0, + mem: 0 + }); + }); + if (callback) { callback(result); } + resolve(result); + } + }); + } + }); + } else { + if (callback) { callback(result); } + resolve(result); + } + } + if (_windows) { + try { + let wincommand = 'Get-WmiObject Win32_Service'; + if (srvs[0] !== '*') { + wincommand += ' -Filter "'; + for (let i = 0; i < srvs.length; i++) { + wincommand += `Name='${srvs[i]}' or `; + } + wincommand = `${wincommand.slice(0, -4)}"`; + } + wincommand += ' | select Name,Caption,Started,StartMode,ProcessId | fl'; + util.powerShell(wincommand).then((stdout, error) => { + if (!error) { + let serviceSections = stdout.split(/\n\s*\n/); + for (let i = 0; i < serviceSections.length; i++) { + if (serviceSections[i].trim() !== '') { + let lines = serviceSections[i].trim().split('\r\n'); + let srvName = util.getValue(lines, 'Name', ':', true).toLowerCase(); + let srvCaption = util.getValue(lines, 'Caption', ':', true).toLowerCase(); + let started = util.getValue(lines, 'Started', ':', true); + let startMode = util.getValue(lines, 'StartMode', ':', true); + let pid = util.getValue(lines, 'ProcessId', ':', true); + if (srvString === '*' || srvs.indexOf(srvName) >= 0 || srvs.indexOf(srvCaption) >= 0) { + result.push({ + name: srvName, + running: (started.toLowerCase() === 'true'), + startmode: startMode, + pids: [pid], + cpu: 0, + mem: 0 + }); + dataSrv.push(srvName); + dataSrv.push(srvCaption); + } + } + } + if (srvString !== '*') { + let srvsMissing = srvs.filter(function (e) { + return dataSrv.indexOf(e) === -1; + }); + srvsMissing.forEach(function (srvName) { + result.push({ + name: srvName, + running: false, + startmode: '', + pids: [], + cpu: 0, + mem: 0 + }); + }); + } + if (callback) { callback(result); } + resolve(result); + } else { + srvs.forEach(function (srvName) { + result.push({ + name: srvName, + running: false, + startmode: '', + cpu: 0, + mem: 0 + }); + }); + if (callback) { callback(result); } + resolve(result); + } + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + } else { + if (callback) { callback([]); } + resolve([]); + } + }); + }); +} + +exports.services = services; + +function parseProcStat(line) { + let parts = line.replace(/ +/g, ' ').split(' '); + let user = (parts.length >= 2 ? parseInt(parts[1]) : 0); + let nice = (parts.length >= 3 ? parseInt(parts[2]) : 0); + let system = (parts.length >= 4 ? parseInt(parts[3]) : 0); + let idle = (parts.length >= 5 ? parseInt(parts[4]) : 0); + let iowait = (parts.length >= 6 ? parseInt(parts[5]) : 0); + let irq = (parts.length >= 7 ? parseInt(parts[6]) : 0); + let softirq = (parts.length >= 8 ? parseInt(parts[7]) : 0); + let steal = (parts.length >= 9 ? parseInt(parts[8]) : 0); + let guest = (parts.length >= 10 ? parseInt(parts[9]) : 0); + let guest_nice = (parts.length >= 11 ? parseInt(parts[10]) : 0); + return user + nice + system + idle + iowait + irq + softirq + steal + guest + guest_nice; +} + +function calcProcStatLinux(line, all, _cpu_old) { + let statparts = line.replace(/ +/g, ' ').split(')'); + if (statparts.length >= 2) { + let parts = statparts[1].split(' '); + if (parts.length >= 16) { + let pid = parseInt(statparts[0].split(' ')[0]); + let utime = parseInt(parts[12]); + let stime = parseInt(parts[13]); + let cutime = parseInt(parts[14]); + let cstime = parseInt(parts[15]); + + // calc + let cpuu = 0; + let cpus = 0; + if (_cpu_old.all > 0 && _cpu_old.list[pid]) { + cpuu = (utime + cutime - _cpu_old.list[pid].utime - _cpu_old.list[pid].cutime) / (all - _cpu_old.all) * 100; // user + cpus = (stime + cstime - _cpu_old.list[pid].stime - _cpu_old.list[pid].cstime) / (all - _cpu_old.all) * 100; // system + } else { + cpuu = (utime + cutime) / (all) * 100; // user + cpus = (stime + cstime) / (all) * 100; // system + } + return { + pid: pid, + utime: utime, + stime: stime, + cutime: cutime, + cstime: cstime, + cpuu: cpuu, + cpus: cpus + }; + } else { + return { + pid: 0, + utime: 0, + stime: 0, + cutime: 0, + cstime: 0, + cpuu: 0, + cpus: 0 + }; + } + } else { + return { + pid: 0, + utime: 0, + stime: 0, + cutime: 0, + cstime: 0, + cpuu: 0, + cpus: 0 + }; + } +} + +function calcProcStatWin(procStat, all, _cpu_old) { + // calc + let cpuu = 0; + let cpus = 0; + if (_cpu_old.all > 0 && _cpu_old.list[procStat.pid]) { + cpuu = (procStat.utime - _cpu_old.list[procStat.pid].utime) / (all - _cpu_old.all) * 100; // user + cpus = (procStat.stime - _cpu_old.list[procStat.pid].stime) / (all - _cpu_old.all) * 100; // system + } else { + cpuu = (procStat.utime) / (all) * 100; // user + cpus = (procStat.stime) / (all) * 100; // system + } + return { + pid: procStat.pid, + utime: cpuu > 0 ? procStat.utime : 0, + stime: cpus > 0 ? procStat.stime : 0, + cpuu: cpuu > 0 ? cpuu : 0, + cpus: cpus > 0 ? cpus : 0 + }; +} + + + +// -------------------------- +// running processes + +function processes(callback) { + + let parsedhead = []; + + function getName(command) { + command = command || ''; + let result = command.split(' ')[0]; + if (result.substr(-1) === ':') { + result = result.substr(0, result.length - 1); + } + if (result.substr(0, 1) !== '[') { + let parts = result.split('/'); + if (isNaN(parseInt(parts[parts.length - 1]))) { + result = parts[parts.length - 1]; + } else { + result = parts[0]; + } + } + return result; + } + + function parseLine(line) { + + let offset = 0; + let offset2 = 0; + + function checkColumn(i) { + offset = offset2; + if (parsedhead[i]) { + offset2 = line.substring(parsedhead[i].to + offset, 10000).indexOf(' '); + } else { + offset2 = 10000; + } + } + + checkColumn(0); + const pid = parseInt(line.substring(parsedhead[0].from + offset, parsedhead[0].to + offset2)); + checkColumn(1); + const ppid = parseInt(line.substring(parsedhead[1].from + offset, parsedhead[1].to + offset2)); + checkColumn(2); + const cpu = parseFloat(line.substring(parsedhead[2].from + offset, parsedhead[2].to + offset2).replace(/,/g, '.')); + checkColumn(3); + const mem = parseFloat(line.substring(parsedhead[3].from + offset, parsedhead[3].to + offset2).replace(/,/g, '.')); + checkColumn(4); + const priority = parseInt(line.substring(parsedhead[4].from + offset, parsedhead[4].to + offset2)); + checkColumn(5); + const vsz = parseInt(line.substring(parsedhead[5].from + offset, parsedhead[5].to + offset2)); + checkColumn(6); + const rss = parseInt(line.substring(parsedhead[6].from + offset, parsedhead[6].to + offset2)); + checkColumn(7); + const nice = parseInt(line.substring(parsedhead[7].from + offset, parsedhead[7].to + offset2)) || 0; + checkColumn(8); + const started = !_sunos ? parseElapsedTime(line.substring(parsedhead[8].from + offset, parsedhead[8].to + offset2).trim()) : parseTimeUnix(line.substring(parsedhead[8].from + offset, parsedhead[8].to + offset2).trim()); + checkColumn(9); + let state = line.substring(parsedhead[9].from + offset, parsedhead[9].to + offset2).trim(); + state = (state[0] === 'R' ? 'running' : (state[0] === 'S' ? 'sleeping' : (state[0] === 'T' ? 'stopped' : (state[0] === 'W' ? 'paging' : (state[0] === 'X' ? 'dead' : (state[0] === 'Z' ? 'zombie' : ((state[0] === 'D' || state[0] === 'U') ? 'blocked' : 'unknown'))))))); + checkColumn(10); + let tty = line.substring(parsedhead[10].from + offset, parsedhead[10].to + offset2).trim(); + if (tty === '?' || tty === '??') { tty = ''; } + checkColumn(11); + const user = line.substring(parsedhead[11].from + offset, parsedhead[11].to + offset2).trim(); + checkColumn(12); + let cmdPath = ''; + let command = ''; + let params = ''; + let fullcommand = line.substring(parsedhead[12].from + offset, parsedhead[12].to + offset2).trim(); + if (fullcommand.substr(fullcommand.length - 1) === ']') { fullcommand = fullcommand.slice(0, -1); } + if (fullcommand.substr(0, 1) === '[') { command = fullcommand.substring(1); } + else { + const p1 = fullcommand.indexOf('('); + const p2 = fullcommand.indexOf(')'); + const p3 = fullcommand.indexOf('/'); + const p4 = fullcommand.indexOf(':'); + if (p1 < p2 && p1 < p3 && p3 < p2) { + command = fullcommand.split(' ')[0]; + command = command.replace(/:/g, ''); + } else { + if (p4 > 0 && (p3 === -1 || p3 > 3)) { + command = fullcommand.split(' ')[0]; + command = command.replace(/:/g, ''); + } else { + // try to figure out where parameter starts + let firstParamPos = fullcommand.indexOf(' -'); + let firstParamPathPos = fullcommand.indexOf(' /'); + firstParamPos = (firstParamPos >= 0 ? firstParamPos : 10000); + firstParamPathPos = (firstParamPathPos >= 0 ? firstParamPathPos : 10000); + const firstPos = Math.min(firstParamPos, firstParamPathPos); + let tmpCommand = fullcommand.substr(0, firstPos); + const tmpParams = fullcommand.substr(firstPos); + const lastSlashPos = tmpCommand.lastIndexOf('/'); + if (lastSlashPos >= 0) { + cmdPath = tmpCommand.substr(0, lastSlashPos); + tmpCommand = tmpCommand.substr(lastSlashPos + 1); + } + + if (firstPos === 10000 && tmpCommand.indexOf(' ') > -1) { + const parts = tmpCommand.split(' '); + if (fs.existsSync(path.join(cmdPath, parts[0]))) { + command = parts.shift(); + params = (parts.join(' ') + ' ' + tmpParams).trim(); + } else { + command = tmpCommand.trim(); + params = tmpParams.trim(); + } + } else { + command = tmpCommand.trim(); + params = tmpParams.trim(); + } + } + } + + } + + return ({ + pid: pid, + parentPid: ppid, + name: _linux ? getName(command) : command, + cpu: cpu, + cpuu: 0, + cpus: 0, + mem: mem, + priority: priority, + memVsz: vsz, + memRss: rss, + nice: nice, + started: started, + state: state, + tty: tty, + user: user, + command: command, + params: params, + path: cmdPath + }); + } + + function parseProcesses(lines) { + let result = []; + if (lines.length > 1) { + let head = lines[0]; + parsedhead = util.parseHead(head, 8); + lines.shift(); + lines.forEach(function (line) { + if (line.trim() !== '') { + result.push(parseLine(line)); + } + }); + } + return result; + } + function parseProcesses2(lines) { + + function formatDateTime(time) { + const month = ('0' + (time.getMonth() + 1).toString()).substr(-2); + const year = time.getFullYear().toString(); + const day = ('0' + time.getDay().toString()).substr(-2); + const hours = time.getHours().toString(); + const mins = time.getMinutes().toString(); + const secs = ('0' + time.getSeconds().toString()).substr(-2); + + return (year + '-' + month + '-' + day + ' ' + hours + ':' + mins + ':' + secs); + } + + let result = []; + lines.forEach(function (line) { + if (line.trim() !== '') { + line = line.trim().replace(/ +/g, ' ').replace(/,+/g, '.'); + const parts = line.split(' '); + const command = parts.slice(9).join(' '); + const pmem = parseFloat((1.0 * parseInt(parts[3]) * 1024 / os.totalmem()).toFixed(1)); + const elapsed_parts = parts[5].split(':'); + const started = formatDateTime(new Date(Date.now() - (elapsed_parts.length > 1 ? (elapsed_parts[0] * 60 + elapsed_parts[1]) * 1000 : elapsed_parts[0] * 1000))); + + result.push({ + pid: parseInt(parts[0]), + parentPid: parseInt(parts[1]), + name: getName(command), + cpu: 0, + cpuu: 0, + cpus: 0, + mem: pmem, + priority: 0, + memVsz: parseInt(parts[2]), + memRss: parseInt(parts[3]), + nice: parseInt(parts[4]), + started: started, + state: (parts[6] === 'R' ? 'running' : (parts[6] === 'S' ? 'sleeping' : (parts[6] === 'T' ? 'stopped' : (parts[6] === 'W' ? 'paging' : (parts[6] === 'X' ? 'dead' : (parts[6] === 'Z' ? 'zombie' : ((parts[6] === 'D' || parts[6] === 'U') ? 'blocked' : 'unknown'))))))), + tty: parts[7], + user: parts[8], + command: command + }); + } + }); + return result; + } + + return new Promise((resolve) => { + process.nextTick(() => { + let result = { + all: 0, + running: 0, + blocked: 0, + sleeping: 0, + unknown: 0, + list: [] + }; + + let cmd = ''; + + if ((_processes_cpu.ms && Date.now() - _processes_cpu.ms >= 500) || _processes_cpu.ms === 0) { + if (_linux || _freebsd || _openbsd || _netbsd || _darwin || _sunos) { + if (_linux) { cmd = 'export LC_ALL=C; ps -axo pid:11,ppid:11,pcpu:6,pmem:6,pri:5,vsz:11,rss:11,ni:5,etime:30,state:5,tty:15,user:20,command; unset LC_ALL'; } + if (_freebsd || _openbsd || _netbsd) { cmd = 'export LC_ALL=C; ps -axo pid,ppid,pcpu,pmem,pri,vsz,rss,ni,etime,state,tty,user,command; unset LC_ALL'; } + if (_darwin) { cmd = 'ps -axo pid,ppid,pcpu,pmem,pri,vsz=temp_title_1,rss=temp_title_2,nice,etime=temp_title_3,state,tty,user,command -r'; } + if (_sunos) { cmd = 'ps -Ao pid,ppid,pcpu,pmem,pri,vsz,rss,nice,stime,s,tty,user,comm'; } + exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) { + if (!error && stdout.toString().trim()) { + result.list = (parseProcesses(stdout.toString().split('\n'))).slice(); + result.all = result.list.length; + result.running = result.list.filter(function (e) { + return e.state === 'running'; + }).length; + result.blocked = result.list.filter(function (e) { + return e.state === 'blocked'; + }).length; + result.sleeping = result.list.filter(function (e) { + return e.state === 'sleeping'; + }).length; + + if (_linux) { + // calc process_cpu - ps is not accurate in linux! + cmd = 'cat /proc/stat | grep "cpu "'; + for (let i = 0; i < result.list.length; i++) { + cmd += (';cat /proc/' + result.list[i].pid + '/stat'); + } + exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) { + let curr_processes = stdout.toString().split('\n'); + + // first line (all - /proc/stat) + let all = parseProcStat(curr_processes.shift()); + + // process + let list_new = {}; + let resultProcess = {}; + for (let i = 0; i < curr_processes.length; i++) { + resultProcess = calcProcStatLinux(curr_processes[i], all, _processes_cpu); + + if (resultProcess.pid) { + + // store pcpu in outer array + let listPos = result.list.map(function (e) { return e.pid; }).indexOf(resultProcess.pid); + if (listPos >= 0) { + result.list[listPos].cpu = resultProcess.cpuu + resultProcess.cpus; + result.list[listPos].cpuu = resultProcess.cpuu; + result.list[listPos].cpus = resultProcess.cpus; + } + + // save new values + list_new[resultProcess.pid] = { + cpuu: resultProcess.cpuu, + cpus: resultProcess.cpus, + utime: resultProcess.utime, + stime: resultProcess.stime, + cutime: resultProcess.cutime, + cstime: resultProcess.cstime + }; + } + } + + // store old values + _processes_cpu.all = all; + // _processes_cpu.list = list_new; + _processes_cpu.list = Object.assign({}, list_new); + _processes_cpu.ms = Date.now() - _processes_cpu.ms; + // _processes_cpu.result = result; + _processes_cpu.result = Object.assign({}, result); + if (callback) { callback(result); } + resolve(result); + }); + } else { + if (callback) { callback(result); } + resolve(result); + } + } else { + cmd = 'ps -o pid,ppid,vsz,rss,nice,etime,stat,tty,user,comm'; + if (_sunos) { + cmd = 'ps -o pid,ppid,vsz,rss,nice,etime,s,tty,user,comm'; + } + exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + lines.shift(); + + result.list = parseProcesses2(lines).slice(); + result.all = result.list.length; + result.running = result.list.filter(function (e) { + return e.state === 'running'; + }).length; + result.blocked = result.list.filter(function (e) { + return e.state === 'blocked'; + }).length; + result.sleeping = result.list.filter(function (e) { + return e.state === 'sleeping'; + }).length; + if (callback) { callback(result); } + resolve(result); + } else { + if (callback) { callback(result); } + resolve(result); + } + }); + } + }); + } else if (_windows) { + try { + util.powerShell('Get-WmiObject Win32_Process | select ProcessId,ParentProcessId,ExecutionState,Caption,CommandLine,ExecutablePath,UserModeTime,KernelModeTime,WorkingSetSize,Priority,PageFileUsage,CreationDate | fl').then((stdout, error) => { + if (!error) { + let processSections = stdout.split(/\n\s*\n/); + let procs = []; + let procStats = []; + let list_new = {}; + let allcpuu = 0; + let allcpus = 0; + // let allcpuu = _processes_cpu.all_utime; + // let allcpus = _processes_cpu.all_stime; + for (let i = 0; i < processSections.length; i++) { + if (processSections[i].trim() !== '') { + let lines = processSections[i].trim().split('\r\n'); + let pid = parseInt(util.getValue(lines, 'ProcessId', ':', true), 10); + let parentPid = parseInt(util.getValue(lines, 'ParentProcessId', ':', true), 10); + let statusValue = util.getValue(lines, 'ExecutionState', ':'); + let name = util.getValue(lines, 'Caption', ':', true); + let commandLine = util.getValue(lines, 'CommandLine', ':', true); + let commandPath = util.getValue(lines, 'ExecutablePath', ':', true); + let utime = parseInt(util.getValue(lines, 'UserModeTime', ':', true), 10); + let stime = parseInt(util.getValue(lines, 'KernelModeTime', ':', true), 10); + let memw = parseInt(util.getValue(lines, 'WorkingSetSize', ':', true), 10); + allcpuu = allcpuu + utime; + allcpus = allcpus + stime; + // allcpuu += utime - (_processes_cpu.list[pid] ? _processes_cpu.list[pid].utime : 0); + // allcpus += stime - (_processes_cpu.list[pid] ? _processes_cpu.list[pid].stime : 0); + result.all++; + if (!statusValue) { result.unknown++; } + if (statusValue === '3') { result.running++; } + if (statusValue === '4' || statusValue === '5') { result.blocked++; } + + procStats.push({ + pid: pid, + utime: utime, + stime: stime, + cpu: 0, + cpuu: 0, + cpus: 0, + }); + procs.push({ + pid: pid, + parentPid: parentPid, + name: name, + cpu: 0, + cpuu: 0, + cpus: 0, + mem: memw / os.totalmem() * 100, + priority: parseInt(util.getValue(lines, 'Priority', ':', true), 10), + memVsz: parseInt(util.getValue(lines, 'PageFileUsage', ':', true), 10), + memRss: Math.floor(parseInt(util.getValue(lines, 'WorkingSetSize', ':', true), 10) / 1024), + nice: 0, + started: parseTimeWin(util.getValue(lines, 'CreationDate', ':', true)), + state: (!statusValue ? _winStatusValues[0] : _winStatusValues[statusValue]), + tty: '', + user: '', + command: commandLine || name, + path: commandPath, + params: '' + }); + } + } + result.sleeping = result.all - result.running - result.blocked - result.unknown; + result.list = procs; + for (let i = 0; i < procStats.length; i++) { + let resultProcess = calcProcStatWin(procStats[i], allcpuu + allcpus, _processes_cpu); + + // store pcpu in outer array + let listPos = result.list.map(function (e) { return e.pid; }).indexOf(resultProcess.pid); + if (listPos >= 0) { + result.list[listPos].cpu = resultProcess.cpuu + resultProcess.cpus; + result.list[listPos].cpuu = resultProcess.cpuu; + result.list[listPos].cpus = resultProcess.cpus; + } + + // save new values + list_new[resultProcess.pid] = { + cpuu: resultProcess.cpuu, + cpus: resultProcess.cpus, + utime: resultProcess.utime, + stime: resultProcess.stime + }; + } + // store old values + _processes_cpu.all = allcpuu + allcpus; + _processes_cpu.all_utime = allcpuu; + _processes_cpu.all_stime = allcpus; + // _processes_cpu.list = list_new; + _processes_cpu.list = Object.assign({}, list_new); + _processes_cpu.ms = Date.now() - _processes_cpu.ms; + // _processes_cpu.result = result; + _processes_cpu.result = Object.assign({}, result); + } + if (callback) { + callback(result); + } + resolve(result); + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } else { + if (callback) { callback(result); } + resolve(result); + } + } else { + if (callback) { callback(_processes_cpu.result); } + resolve(_processes_cpu.result); + } + }); + }); +} + +exports.processes = processes; + +// -------------------------- +// PS - process load +// get detailed information about a certain process +// (PID, CPU-Usage %, Mem-Usage %) + +function processLoad(proc, callback) { + + // fallback - if only callback is given + if (util.isFunction(proc) && !callback) { + callback = proc; + proc = ''; + } + + return new Promise((resolve) => { + process.nextTick(() => { + + proc = proc || ''; + + if (typeof proc !== 'string') { + if (callback) { callback([]); } + return resolve([]); + } + + let processesString = ''; + processesString.__proto__.toLowerCase = util.stringToLower; + processesString.__proto__.replace = util.stringReplace; + processesString.__proto__.trim = util.stringTrim; + + const s = util.sanitizeShellString(proc); + for (let i = 0; i <= util.mathMin(s.length, 2000); i++) { + if (!(s[i] === undefined)) { + processesString = processesString + s[i]; + } + } + + processesString = processesString.trim().toLowerCase().replace(/, /g, '|').replace(/,+/g, '|'); + if (processesString === '') { + processesString = '*'; + } + if (util.isPrototypePolluted() && processesString !== '*') { + processesString = '------'; + } + let processes = processesString.split('|'); + let result = []; + + const procSanitized = util.isPrototypePolluted() ? '' : util.sanitizeShellString(proc); + + // from here new + // let result = { + // 'proc': procSanitized, + // 'pid': null, + // 'cpu': 0, + // 'mem': 0 + // }; + if (procSanitized && processes.length && processes[0] !== '------') { + if (_windows) { + try { + util.powerShell('Get-WmiObject Win32_Process | select ProcessId,Caption,UserModeTime,KernelModeTime,WorkingSetSize | fl').then((stdout, error) => { + if (!error) { + let processSections = stdout.split(/\n\s*\n/); + let procStats = []; + let list_new = {}; + let allcpuu = 0; + let allcpus = 0; + // let allcpuu = _process_cpu.all_utime; + // let allcpus = _process_cpu.all_stime; + + // go through all processes + for (let i = 0; i < processSections.length; i++) { + if (processSections[i].trim() !== '') { + let lines = processSections[i].trim().split('\r\n'); + let pid = parseInt(util.getValue(lines, 'ProcessId', ':', true), 10); + let name = util.getValue(lines, 'Caption', ':', true); + let utime = parseInt(util.getValue(lines, 'UserModeTime', ':', true), 10); + let stime = parseInt(util.getValue(lines, 'KernelModeTime', ':', true), 10); + let mem = parseInt(util.getValue(lines, 'WorkingSetSize', ':', true), 10); + allcpuu = allcpuu + utime; + allcpus = allcpus + stime; + // allcpuu += utime - (_process_cpu.list[pid] ? _process_cpu.list[pid].utime : 0); + // allcpus += stime - (_process_cpu.list[pid] ? _process_cpu.list[pid].stime : 0); + + procStats.push({ + pid: pid, + name, + utime: utime, + stime: stime, + cpu: 0, + cpuu: 0, + cpus: 0, + mem + }); + let pname = ''; + let inList = false; + processes.forEach(function (proc) { + // console.log(proc) + // console.log(item) + // inList = inList || item.name.toLowerCase() === proc.toLowerCase(); + if (name.toLowerCase().indexOf(proc.toLowerCase()) >= 0 && !inList) { + inList = true; + pname = proc; + } + }); + + if (processesString === '*' || inList) { + let processFound = false; + result.forEach(function (item) { + if (item.proc.toLowerCase() === pname.toLowerCase()) { + item.pids.push(pid); + item.mem += mem / os.totalmem() * 100; + processFound = true; + } + }); + if (!processFound) { + result.push({ + proc: pname, + pid: pid, + pids: [pid], + cpu: 0, + mem: mem / os.totalmem() * 100 + }); + } + } + } + } + // add missing processes + if (processesString !== '*') { + let processesMissing = processes.filter(function (name) { + // return procStats.filter(function(item) { return item.name.toLowerCase() === name }).length === 0; + return procStats.filter(function (item) { return item.name.toLowerCase().indexOf(name) >= 0; }).length === 0; + + }); + processesMissing.forEach(function (procName) { + result.push({ + proc: procName, + pid: null, + pids: [], + cpu: 0, + mem: 0 + }); + }); + } + + // calculate proc stats for each proc + for (let i = 0; i < procStats.length; i++) { + let resultProcess = calcProcStatWin(procStats[i], allcpuu + allcpus, _process_cpu); + + let listPos = -1; + for (let j = 0; j < result.length; j++) { + if (result[j].pid === resultProcess.pid || result[j].pids.indexOf(resultProcess.pid) >= 0) { listPos = j; } + } + if (listPos >= 0) { + result[listPos].cpu += resultProcess.cpuu + resultProcess.cpus; + } + + // save new values + list_new[resultProcess.pid] = { + cpuu: resultProcess.cpuu, + cpus: resultProcess.cpus, + utime: resultProcess.utime, + stime: resultProcess.stime + }; + } + // store old values + _process_cpu.all = allcpuu + allcpus; + _process_cpu.all_utime = allcpuu; + _process_cpu.all_stime = allcpus; + // _process_cpu.list = list_new; + _process_cpu.list = Object.assign({}, list_new); + _process_cpu.ms = Date.now() - _process_cpu.ms; + _process_cpu.result = JSON.parse(JSON.stringify(result)); + if (callback) { + callback(result); + } + resolve(result); + } + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + + if (_darwin || _linux || _freebsd || _openbsd || _netbsd) { + const params = ['-axo', 'pid,pcpu,pmem,comm']; + util.execSafe('ps', params).then((stdout) => { + if (stdout) { + let procStats = []; + let lines = stdout.toString().split('\n').filter(function (line) { + if (processesString === '*') { return true; } + if (line.toLowerCase().indexOf('grep') !== -1) { return false; } // remove this?? + let found = false; + processes.forEach(function (item) { + found = found || (line.toLowerCase().indexOf(item.toLowerCase()) >= 0); + }); + return found; + }); + + lines.forEach(function (line) { + let data = line.trim().replace(/ +/g, ' ').split(' '); + if (data.length > 3) { + procStats.push({ + name: data[3].substring(data[3].lastIndexOf('/') + 1), + pid: parseInt(data[0]) || 0, + cpu: parseFloat(data[1].replace(',', '.')), + mem: parseFloat(data[2].replace(',', '.')) + }); + } + }); + + procStats.forEach(function (item) { + let listPos = -1; + let inList = false; + let name = ''; + for (let j = 0; j < result.length; j++) { + // if (result[j].proc.toLowerCase() === item.name.toLowerCase()) { + // if (result[j].proc.toLowerCase().indexOf(item.name.toLowerCase()) >= 0) { + if (item.name.toLowerCase().indexOf(result[j].proc.toLowerCase()) >= 0) { + listPos = j; + } + } + // console.log(listPos); + processes.forEach(function (proc) { + // console.log(proc) + // console.log(item) + // inList = inList || item.name.toLowerCase() === proc.toLowerCase(); + if (item.name.toLowerCase().indexOf(proc.toLowerCase()) >= 0 && !inList) { + inList = true; + name = proc; + } + }); + // console.log(item); + // console.log(listPos); + if ((processesString === '*') || inList) { + if (listPos < 0) { + result.push({ + proc: name, + pid: item.pid, + pids: [item.pid], + cpu: item.cpu, + mem: item.mem + }); + } else { + result[listPos].pids.push(item.pid); + result[listPos].cpu += item.cpu; + result[listPos].mem += item.mem; + } + } + }); + + if (processesString !== '*') { + // add missing processes + let processesMissing = processes.filter(function (name) { + return procStats.filter(function (item) { return item.name.toLowerCase().indexOf(name) >= 0; }).length === 0; + }); + processesMissing.forEach(function (procName) { + result.push({ + proc: procName, + pid: null, + pids: [], + cpu: 0, + mem: 0 + }); + }); + } + if (_linux) { + // calc process_cpu - ps is not accurate in linux! + result.forEach(function (item) { + item.cpu = 0; + }); + let cmd = 'cat /proc/stat | grep "cpu "'; + for (let i in result) { + for (let j in result[i].pids) { + cmd += (';cat /proc/' + result[i].pids[j] + '/stat'); + } + } + exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) { + let curr_processes = stdout.toString().split('\n'); + + // first line (all - /proc/stat) + let all = parseProcStat(curr_processes.shift()); + + // process + let list_new = {}; + let resultProcess = {}; + + for (let i = 0; i < curr_processes.length; i++) { + resultProcess = calcProcStatLinux(curr_processes[i], all, _process_cpu); + + if (resultProcess.pid) { + + // find result item + let resultItemId = -1; + for (let i in result) { + if (result[i].pids.indexOf(resultProcess.pid) >= 0) { + resultItemId = i; + } + } + // store pcpu in outer result + if (resultItemId >= 0) { + result[resultItemId].cpu += resultProcess.cpuu + resultProcess.cpus; + } + + // save new values + list_new[resultProcess.pid] = { + cpuu: resultProcess.cpuu, + cpus: resultProcess.cpus, + utime: resultProcess.utime, + stime: resultProcess.stime, + cutime: resultProcess.cutime, + cstime: resultProcess.cstime + }; + } + } + + result.forEach(function (item) { + item.cpu = Math.round(item.cpu * 100) / 100; + }); + + _process_cpu.all = all; + // _process_cpu.list = list_new; + _process_cpu.list = Object.assign({}, list_new); + _process_cpu.ms = Date.now() - _process_cpu.ms; + // _process_cpu.result = result; + _process_cpu.result = Object.assign({}, result); + if (callback) { callback(result); } + resolve(result); + }); + } else { + if (callback) { callback(result); } + resolve(result); + } + } else { + if (callback) { callback(result); } + resolve(result); + } + }); + } + } + }); + }); +} + +exports.processLoad = processLoad; diff --git a/node_modules/systeminformation/lib/system.js b/node_modules/systeminformation/lib/system.js new file mode 100644 index 0000000..7d1c3f8 --- /dev/null +++ b/node_modules/systeminformation/lib/system.js @@ -0,0 +1,829 @@ +'use strict'; +// @ts-check +// ================================================================================== +// system.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 2. System (Hardware, BIOS, Base Board) +// ---------------------------------------------------------------------------------- + +const fs = require('fs'); +const os = require('os'); +const util = require('./util'); +const exec = require('child_process').exec; +const execSync = require('child_process').execSync; +const execPromise = util.promisify(require('child_process').exec); + +let _platform = process.platform; + +const _linux = (_platform === 'linux' || _platform === 'android'); +const _darwin = (_platform === 'darwin'); +const _windows = (_platform === 'win32'); +const _freebsd = (_platform === 'freebsd'); +const _openbsd = (_platform === 'openbsd'); +const _netbsd = (_platform === 'netbsd'); +const _sunos = (_platform === 'sunos'); + +function system(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + + let result = { + manufacturer: '', + model: 'Computer', + version: '', + serial: '-', + uuid: '-', + sku: '-', + virtual: false + }; + + if (_linux || _freebsd || _openbsd || _netbsd) { + exec('export LC_ALL=C; dmidecode -t system 2>/dev/null; unset LC_ALL', function (error, stdout) { + // if (!error) { + let lines = stdout.toString().split('\n'); + result.manufacturer = util.getValue(lines, 'manufacturer'); + result.model = util.getValue(lines, 'product name'); + result.version = util.getValue(lines, 'version'); + result.serial = util.getValue(lines, 'serial number'); + result.uuid = util.getValue(lines, 'uuid').toLowerCase(); + result.sku = util.getValue(lines, 'sku number'); + // } + // Non-Root values + const cmd = `echo -n "product_name: "; cat /sys/devices/virtual/dmi/id/product_name 2>/dev/null; echo; + echo -n "product_serial: "; cat /sys/devices/virtual/dmi/id/product_serial 2>/dev/null; echo; + echo -n "product_uuid: "; cat /sys/devices/virtual/dmi/id/product_uuid 2>/dev/null; echo; + echo -n "product_version: "; cat /sys/devices/virtual/dmi/id/product_version 2>/dev/null; echo; + echo -n "sys_vendor: "; cat /sys/devices/virtual/dmi/id/sys_vendor 2>/dev/null; echo;`; + try { + lines = execSync(cmd).toString().split('\n'); + result.manufacturer = result.manufacturer === '' ? util.getValue(lines, 'sys_vendor') : result.manufacturer; + result.model = result.model === '' ? util.getValue(lines, 'product_name') : result.model; + result.version = result.version === '' ? util.getValue(lines, 'product_version') : result.version; + result.serial = result.serial === '' ? util.getValue(lines, 'product_serial') : result.serial; + result.uuid = result.uuid === '' ? util.getValue(lines, 'product_uuid').toLowerCase() : result.uuid; + } catch (e) { + util.noop(); + } + if (!result.serial || result.serial.toLowerCase().indexOf('o.e.m.') !== -1) { result.serial = '-'; } + if (!result.manufacturer || result.manufacturer.toLowerCase().indexOf('o.e.m.') !== -1) { result.manufacturer = ''; } + if (!result.model || result.model.toLowerCase().indexOf('o.e.m.') !== -1) { result.model = 'Computer'; } + if (!result.version || result.version.toLowerCase().indexOf('o.e.m.') !== -1) { result.version = ''; } + if (!result.sku || result.sku.toLowerCase().indexOf('o.e.m.') !== -1) { result.sku = '-'; } + + // detect virtual (1) + if (result.model.toLowerCase() === 'virtualbox' || result.model.toLowerCase() === 'kvm' || result.model.toLowerCase() === 'virtual machine' || result.model.toLowerCase() === 'bochs' || result.model.toLowerCase().startsWith('vmware') || result.model.toLowerCase().startsWith('droplet')) { + result.virtual = true; + switch (result.model.toLowerCase()) { + case 'virtualbox': + result.virtualHost = 'VirtualBox'; + break; + case 'vmware': + result.virtualHost = 'VMware'; + break; + case 'kvm': + result.virtualHost = 'KVM'; + break; + case 'bochs': + result.virtualHost = 'bochs'; + break; + } + } + if (result.manufacturer.toLowerCase().startsWith('vmware') || result.manufacturer.toLowerCase() === 'xen') { + result.virtual = true; + switch (result.manufacturer.toLowerCase()) { + case 'vmware': + result.virtualHost = 'VMware'; + break; + case 'xen': + result.virtualHost = 'Xen'; + break; + } + } + if (!result.virtual) { + try { + const disksById = execSync('ls -1 /dev/disk/by-id/ 2>/dev/null').toString(); + if (disksById.indexOf('_QEMU_') >= 0) { + result.virtual = true; + result.virtualHost = 'QEMU'; + } + if (disksById.indexOf('_VBOX_') >= 0) { + result.virtual = true; + result.virtualHost = 'VirtualBox'; + } + } catch (e) { + util.noop(); + } + } + if (!result.virtual && (os.release().toLowerCase().indexOf('microsoft') >= 0 || os.release().toLowerCase().endsWith('wsl2'))) { + const kernelVersion = parseFloat(os.release().toLowerCase()); + result.virtual = true; + result.manufacturer = 'Microsoft'; + result.model = 'WSL'; + result.version = kernelVersion < 4.19 ? '1' : '2'; + } + if ((_freebsd || _openbsd || _netbsd) && !result.virtualHost) { + try { + const procInfo = execSync('dmidecode -t 4'); + const procLines = procInfo.toString().split('\n'); + const procManufacturer = util.getValue(procLines, 'manufacturer', ':', true); + switch (procManufacturer.toLowerCase()) { + case 'virtualbox': + result.virtualHost = 'VirtualBox'; + break; + case 'vmware': + result.virtualHost = 'VMware'; + break; + case 'kvm': + result.virtualHost = 'KVM'; + break; + case 'bochs': + result.virtualHost = 'bochs'; + break; + } + } catch (e) { + util.noop(); + } + } + // detect docker + if (fs.existsSync('/.dockerenv') || fs.existsSync('/.dockerinit')) { + result.model = 'Docker Container'; + } + try { + const stdout = execSync('dmesg 2>/dev/null | grep -iE "virtual|hypervisor" | grep -iE "vmware|qemu|kvm|xen" | grep -viE "Nested Virtualization|/virtual/"'); + // detect virtual machines + let lines = stdout.toString().split('\n'); + if (lines.length > 0) { + if (result.model === 'Computer') { result.model = 'Virtual machine'; } + result.virtual = true; + if (stdout.toString().toLowerCase().indexOf('vmware') >= 0 && !result.virtualHost) { + result.virtualHost = 'VMware'; + } + if (stdout.toString().toLowerCase().indexOf('qemu') >= 0 && !result.virtualHost) { + result.virtualHost = 'QEMU'; + } + if (stdout.toString().toLowerCase().indexOf('xen') >= 0 && !result.virtualHost) { + result.virtualHost = 'Xen'; + } + if (stdout.toString().toLowerCase().indexOf('kvm') >= 0 && !result.virtualHost) { + result.virtualHost = 'KVM'; + } + } + } catch (e) { + util.noop(); + } + + if (result.manufacturer === '' && result.model === 'Computer' && result.version === '') { + // Check Raspberry Pi + fs.readFile('/proc/cpuinfo', function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + result.model = util.getValue(lines, 'hardware', ':', true).toUpperCase(); + result.version = util.getValue(lines, 'revision', ':', true).toLowerCase(); + result.serial = util.getValue(lines, 'serial', ':', true); + const model = util.getValue(lines, 'model:', ':', true); + // reference values: https://elinux.org/RPi_HardwareHistory + // https://www.raspberrypi.org/documentation/hardware/raspberrypi/revision-codes/README.md + if ((result.model === 'BCM2835' || result.model === 'BCM2708' || result.model === 'BCM2709' || result.model === 'BCM2710' || result.model === 'BCM2711' || result.model === 'BCM2836' || result.model === 'BCM2837') && model.toLowerCase().indexOf('raspberry') >= 0) { + const rPIRevision = util.decodePiCpuinfo(lines); + result.model = rPIRevision.model; + result.version = rPIRevision.revisionCode; + result.manufacturer = 'Raspberry Pi Foundation'; + result.raspberry = { + manufacturer: rPIRevision.manufacturer, + processor: rPIRevision.processor, + type: rPIRevision.type, + revision: rPIRevision.revision + }; + } + + // if (result.model === 'BCM2835' || result.model === 'BCM2708' || result.model === 'BCM2709' || result.model === 'BCM2835' || result.model === 'BCM2837') { + + + // // Pi 4 + // if (['d03114'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi 4 Model B'; + // result.version = result.version + ' - Rev. 1.4'; + // } + // if (['b03112', 'c03112'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi 4 Model B'; + // result.version = result.version + ' - Rev. 1.2'; + // } + // if (['a03111', 'b03111', 'c03111'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi 4 Model B'; + // result.version = result.version + ' - Rev. 1.1'; + // } + // // Pi 3 + // if (['a02082', 'a22082', 'a32082', 'a52082'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi 3 Model B'; + // result.version = result.version + ' - Rev. 1.2'; + // } + // if (['a22083'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi 3 Model B'; + // result.version = result.version + ' - Rev. 1.3'; + // } + // if (['a020d3'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi 3 Model B+'; + // result.version = result.version + ' - Rev. 1.3'; + // } + // if (['9020e0'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi 3 Model A+'; + // result.version = result.version + ' - Rev. 1.3'; + // } + // // Pi 2 Model B + // if (['a01040'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi 2 Model B'; + // result.version = result.version + ' - Rev. 1.0'; + // } + // if (['a01041', 'a21041'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi 2 Model B'; + // result.version = result.version + ' - Rev. 1.1'; + // } + // if (['a22042', 'a02042'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi 2 Model B'; + // result.version = result.version + ' - Rev. 1.2'; + // } + + // // Compute Model + // if (['a02100'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi CM3+'; + // result.version = result.version + ' - Rev 1.0'; + // } + // if (['a020a0', 'a220a0'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi CM3'; + // result.version = result.version + ' - Rev 1.0'; + // } + // if (['900061'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi CM'; + // result.version = result.version + ' - Rev 1.1'; + // } + + // // Pi Zero + // if (['900092', '920092'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi Zero'; + // result.version = result.version + ' - Rev 1.2'; + // } + // if (['900093', '920093'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi Zero'; + // result.version = result.version + ' - Rev 1.3'; + // } + // if (['9000c1'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi Zero W'; + // result.version = result.version + ' - Rev 1.1'; + // } + + // // A, B, A+ B+ + // if (['0002', '0003'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi Model B'; + // result.version = result.version + ' - Rev 1.0'; + // } + // if (['0004', '0005', '0006', '000d', '000e', '000f'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi Model B'; + // result.version = result.version + ' - Rev 2.0'; + // } + // if (['0007', '0008', '0009'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi Model A'; + // result.version = result.version + ' - Rev 2.0'; + // } + // if (['0010'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi Model B+'; + // result.version = result.version + ' - Rev 1.0'; + // } + // if (['0012'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi Model A+'; + // result.version = result.version + ' - Rev 1.0'; + // } + // if (['0013', '900032'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi Model B+'; + // result.version = result.version + ' - Rev 1.2'; + // } + // if (['0015', '900021'].indexOf(result.version) >= 0) { + // result.model = result.model + ' - Pi Model A+'; + // result.version = result.version + ' - Rev 1.1'; + // } + // if (result.model.indexOf('Pi') !== -1 && result.version) { // Pi, Pi Zero + // result.manufacturer = 'Raspberry Pi Foundation'; + // } + // } + } + if (callback) { callback(result); } + resolve(result); + }); + } else { + if (callback) { callback(result); } + resolve(result); + } + }); + } + if (_darwin) { + exec('ioreg -c IOPlatformExpertDevice -d 2', function (error, stdout) { + if (!error) { + let lines = stdout.toString().replace(/[<>"]/g, '').split('\n'); + result.manufacturer = util.getValue(lines, 'manufacturer', '=', true); + result.model = util.getValue(lines, 'model', '=', true); + result.version = util.getValue(lines, 'version', '=', true); + result.serial = util.getValue(lines, 'ioplatformserialnumber', '=', true); + result.uuid = util.getValue(lines, 'ioplatformuuid', '=', true).toLowerCase(); + result.sku = util.getValue(lines, 'board-id', '=', true); + } + if (callback) { callback(result); } + resolve(result); + }); + } + if (_sunos) { + if (callback) { callback(result); } + resolve(result); + } + if (_windows) { + try { + util.powerShell('Get-WmiObject Win32_ComputerSystemProduct | select Name,Vendor,Version,IdentifyingNumber,UUID | fl').then((stdout, error) => { + if (!error) { + // let lines = stdout.split('\r\n').filter(line => line.trim() !== '').filter((line, idx) => idx > 0)[0].trim().split(/\s\s+/); + let lines = stdout.split('\r\n'); + result.manufacturer = util.getValue(lines, 'vendor', ':'); + result.model = util.getValue(lines, 'name', ':'); + result.version = util.getValue(lines, 'version', ':'); + result.serial = util.getValue(lines, 'identifyingnumber', ':'); + result.uuid = util.getValue(lines, 'uuid', ':').toLowerCase(); + // detect virtual (1) + const model = result.model.toLowerCase(); + if (model === 'virtualbox' || model === 'kvm' || model === 'virtual machine' || model === 'bochs' || model.startsWith('vmware') || model.startsWith('qemu')) { + result.virtual = true; + if (model.startsWith('virtualbox')) { result.virtualHost = 'VirtualBox'; } + if (model.startsWith('vmware')) { result.virtualHost = 'VMware'; } + if (model.startsWith('kvm')) { result.virtualHost = 'KVM'; } + if (model.startsWith('bochs')) { result.virtualHost = 'bochs'; } + if (model.startsWith('qemu')) { result.virtualHost = 'KVM'; } + } + const manufacturer = result.manufacturer.toLowerCase(); + if (manufacturer.startsWith('vmware') || manufacturer.startsWith('qemu') || manufacturer === 'xen') { + result.virtual = true; + if (manufacturer.startsWith('vmware')) { result.virtualHost = 'VMware'; } + if (manufacturer.startsWith('xen')) { result.virtualHost = 'Xen'; } + if (manufacturer.startsWith('qemu')) { result.virtualHost = 'KVM'; } + } + util.powerShell('Get-WmiObject MS_Systeminformation -Namespace "root/wmi" | select systemsku | fl ').then((stdout, error) => { + if (!error) { + let lines = stdout.split('\r\n'); + result.sku = util.getValue(lines, 'systemsku', ':'); + } + if (!result.virtual) { + util.powerShell('Get-WmiObject Win32_bios | select Version, SerialNumber, SMBIOSBIOSVersion').then((stdout, error) => { + if (!error) { + let lines = stdout.toString(); + if (lines.indexOf('VRTUAL') >= 0 || lines.indexOf('A M I ') >= 0 || lines.indexOf('VirtualBox') >= 0 || lines.indexOf('VMWare') >= 0 || lines.indexOf('Xen') >= 0) { + result.virtual = true; + if (lines.indexOf('VirtualBox') >= 0 && !result.virtualHost) { + result.virtualHost = 'VirtualBox'; + } + if (lines.indexOf('VMware') >= 0 && !result.virtualHost) { + result.virtualHost = 'VMware'; + } + if (lines.indexOf('Xen') >= 0 && !result.virtualHost) { + result.virtualHost = 'Xen'; + } + if (lines.indexOf('VRTUAL') >= 0 && !result.virtualHost) { + result.virtualHost = 'Hyper-V'; + } + if (lines.indexOf('A M I') >= 0 && !result.virtualHost) { + result.virtualHost = 'Virtual PC'; + } + } + if (callback) { callback(result); } + resolve(result); + } else { + if (callback) { callback(result); } + resolve(result); + } + }); + } else { + if (callback) { callback(result); } + resolve(result); + } + }); + } else { + if (callback) { callback(result); } + resolve(result); + } + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + }); + }); +} + +exports.system = system; + +function bios(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + + let result = { + vendor: '', + version: '', + releaseDate: '', + revision: '', + }; + let cmd = ''; + if (_linux || _freebsd || _openbsd || _netbsd) { + if (process.arch === 'arm') { + cmd = 'cat /proc/cpuinfo | grep Serial'; + } else { + cmd = 'export LC_ALL=C; dmidecode -t bios 2>/dev/null; unset LC_ALL'; + } + exec(cmd, function (error, stdout) { + let lines = stdout.toString().split('\n'); + result.vendor = util.getValue(lines, 'Vendor'); + result.version = util.getValue(lines, 'Version'); + let datetime = util.getValue(lines, 'Release Date'); + result.releaseDate = util.parseDateTime(datetime).date; + result.revision = util.getValue(lines, 'BIOS Revision'); + result.serial = util.getValue(lines, 'SerialNumber'); + let language = util.getValue(lines, 'Currently Installed Language').split('|')[0]; + if (language) { + result.language = language; + } + if (lines.length && stdout.toString().indexOf('Characteristics:') >= 0) { + const features = []; + lines.forEach(line => { + if (line.indexOf(' is supported') >= 0) { + const feature = line.split(' is supported')[0].trim(); + features.push(feature); + } + }); + result.features = features; + } + // Non-Root values + const cmd = `echo -n "bios_date: "; cat /sys/devices/virtual/dmi/id/bios_date 2>/dev/null; echo; + echo -n "bios_vendor: "; cat /sys/devices/virtual/dmi/id/bios_vendor 2>/dev/null; echo; + echo -n "bios_version: "; cat /sys/devices/virtual/dmi/id/bios_version 2>/dev/null; echo;`; + try { + lines = execSync(cmd).toString().split('\n'); + result.vendor = !result.vendor ? util.getValue(lines, 'bios_vendor') : result.vendor; + result.version = !result.version ? util.getValue(lines, 'bios_version') : result.version; + datetime = util.getValue(lines, 'bios_date'); + result.releaseDate = !result.releaseDate ? util.parseDateTime(datetime).date : result.releaseDate; + } catch (e) { + util.noop(); + } + if (callback) { callback(result); } + resolve(result); + }); + } + if (_darwin) { + result.vendor = 'Apple Inc.'; + exec( + 'system_profiler SPHardwareDataType -json', function (error, stdout) { + try { + const hardwareData = JSON.parse(stdout.toString()); + if (hardwareData && hardwareData.SPHardwareDataType && hardwareData.SPHardwareDataType.length) { + let bootRomVersion = hardwareData.SPHardwareDataType[0].boot_rom_version; + bootRomVersion = bootRomVersion ? bootRomVersion.split('(')[0].trim() : null; + result.version = bootRomVersion; + } + } catch (e) { + util.noop(); + } + if (callback) { callback(result); } + resolve(result); + }); + } + if (_sunos) { + result.vendor = 'Sun Microsystems'; + if (callback) { callback(result); } + resolve(result); + } + if (_windows) { + try { + util.powerShell('Get-WmiObject Win32_bios | select Description,Version,Manufacturer,ReleaseDate,BuildNumber,SerialNumber | fl').then((stdout, error) => { + if (!error) { + let lines = stdout.toString().split('\r\n'); + const description = util.getValue(lines, 'description', ':'); + if (description.indexOf(' Version ') !== -1) { + // ... Phoenix ROM BIOS PLUS Version 1.10 A04 + result.vendor = description.split(' Version ')[0].trim(); + result.version = description.split(' Version ')[1].trim(); + } else if (description.indexOf(' Ver: ') !== -1) { + // ... BIOS Date: 06/27/16 17:50:16 Ver: 1.4.5 + result.vendor = util.getValue(lines, 'manufacturer', ':'); + result.version = description.split(' Ver: ')[1].trim(); + } else { + result.vendor = util.getValue(lines, 'manufacturer', ':'); + result.version = util.getValue(lines, 'version', ':'); + } + result.releaseDate = util.getValue(lines, 'releasedate', ':'); + if (result.releaseDate.length >= 10) { + result.releaseDate = result.releaseDate.substr(0, 4) + '-' + result.releaseDate.substr(4, 2) + '-' + result.releaseDate.substr(6, 2); + } + result.revision = util.getValue(lines, 'buildnumber', ':'); + result.serial = util.getValue(lines, 'serialnumber', ':'); + } + + if (callback) { callback(result); } + resolve(result); + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + }); + }); +} + +exports.bios = bios; + +function baseboard(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + + let result = { + manufacturer: '', + model: '', + version: '', + serial: '-', + assetTag: '-', + memMax: null, + memSlots: null + }; + let cmd = ''; + if (_linux || _freebsd || _openbsd || _netbsd) { + if (process.arch === 'arm') { + cmd = 'cat /proc/cpuinfo | grep Serial'; + // 'BCM2709', 'BCM2835', 'BCM2708' --> + } else { + cmd = 'export LC_ALL=C; dmidecode -t 2 2>/dev/null; unset LC_ALL'; + } + const workload = []; + workload.push(execPromise(cmd)); + workload.push(execPromise('export LC_ALL=C; dmidecode -t memory 2>/dev/null')); + util.promiseAll( + workload + ).then(data => { + let lines = data.results[0] ? data.results[0].toString().split('\n') : ['']; + result.manufacturer = util.getValue(lines, 'Manufacturer'); + result.model = util.getValue(lines, 'Product Name'); + result.version = util.getValue(lines, 'Version'); + result.serial = util.getValue(lines, 'Serial Number'); + result.assetTag = util.getValue(lines, 'Asset Tag'); + // Non-Root values + const cmd = `echo -n "board_asset_tag: "; cat /sys/devices/virtual/dmi/id/board_asset_tag 2>/dev/null; echo; + echo -n "board_name: "; cat /sys/devices/virtual/dmi/id/board_name 2>/dev/null; echo; + echo -n "board_serial: "; cat /sys/devices/virtual/dmi/id/board_serial 2>/dev/null; echo; + echo -n "board_vendor: "; cat /sys/devices/virtual/dmi/id/board_vendor 2>/dev/null; echo; + echo -n "board_version: "; cat /sys/devices/virtual/dmi/id/board_version 2>/dev/null; echo;`; + try { + lines = execSync(cmd).toString().split('\n'); + result.manufacturer = !result.manufacturer ? util.getValue(lines, 'board_vendor') : result.manufacturer; + result.model = !result.model ? util.getValue(lines, 'board_name') : result.model; + result.version = !result.version ? util.getValue(lines, 'board_version') : result.version; + result.serial = !result.serial ? util.getValue(lines, 'board_serial') : result.serial; + result.assetTag = !result.assetTag ? util.getValue(lines, 'board_asset_tag') : result.assetTag; + } catch (e) { + util.noop(); + } + if (result.serial.toLowerCase().indexOf('o.e.m.') !== -1) { result.serial = '-'; } + if (result.assetTag.toLowerCase().indexOf('o.e.m.') !== -1) { result.assetTag = '-'; } + + // mem + lines = data.results[1] ? data.results[1].toString().split('\n') : ['']; + result.memMax = util.toInt(util.getValue(lines, 'Maximum Capacity')) * 1024 * 1024 * 1024 || null; + result.memSlots = util.toInt(util.getValue(lines, 'Number Of Devices')) || null; + + // raspberry + let linesRpi = ''; + try { + linesRpi = fs.readFileSync('/proc/cpuinfo').toString().split('\n'); + } catch (e) { + util.noop(); + } + const hardware = util.getValue(linesRpi, 'hardware'); + if (hardware.startsWith('BCM')) { + const rpi = util.decodePiCpuinfo(linesRpi); + result.manufacturer = rpi.manufacturer; + result.model = 'Raspberry Pi'; + result.serial = rpi.serial; + result.version = rpi.type + ' - ' + rpi.revision; + result.memMax = os.totalmem(); + result.memSlots = 0; + } + + if (callback) { callback(result); } + resolve(result); + }); + } + if (_darwin) { + const workload = []; + workload.push(execPromise('ioreg -c IOPlatformExpertDevice -d 2')); + workload.push(execPromise('system_profiler SPMemoryDataType')); + util.promiseAll( + workload + ).then(data => { + let lines = data.results[0] ? data.results[0].toString().replace(/[<>"]/g, '').split('\n') : ['']; + result.manufacturer = util.getValue(lines, 'manufacturer', '=', true); + result.model = util.getValue(lines, 'model', '=', true); + result.version = util.getValue(lines, 'version', '=', true); + result.serial = util.getValue(lines, 'ioplatformserialnumber', '=', true); + result.assetTag = util.getValue(lines, 'board-id', '=', true); + + // mem + let devices = data.results[1] ? data.results[1].toString().split(' BANK ') : ['']; + if (devices.length === 1) { + devices = data.results[1] ? data.results[1].toString().split(' DIMM') : ['']; + } + devices.shift(); + result.memSlots = devices.length; + + if (os.arch() === 'arm64') { + result.memSlots = 0; + result.memMax = os.totalmem(); + } + + if (callback) { callback(result); } + resolve(result); + }); + } + if (_sunos) { + if (callback) { callback(result); } + resolve(result); + } + if (_windows) { + try { + const workload = []; + workload.push(util.powerShell('Get-WmiObject Win32_baseboard | select Model,Manufacturer,Product,Version,SerialNumber,PartNumber,SKU | fl')); + workload.push(util.powerShell('Get-WmiObject Win32_physicalmemoryarray | select MaxCapacity, MemoryDevices | fl')); + util.promiseAll( + workload + ).then(data => { + let lines = data.results[0] ? data.results[0].toString().split('\r\n') : ['']; + + result.manufacturer = util.getValue(lines, 'manufacturer', ':'); + result.model = util.getValue(lines, 'model', ':'); + if (!result.model) { + result.model = util.getValue(lines, 'product', ':'); + } + result.version = util.getValue(lines, 'version', ':'); + result.serial = util.getValue(lines, 'serialnumber', ':'); + result.assetTag = util.getValue(lines, 'partnumber', ':'); + if (!result.assetTag) { + result.assetTag = util.getValue(lines, 'sku', ':'); + } + + // memphysical + lines = data.results[1] ? data.results[1].toString().split('\r\n') : ['']; + result.memMax = util.toInt(util.getValue(lines, 'MaxCapacity', ':')) || null; + result.memSlots = util.toInt(util.getValue(lines, 'MemoryDevices', ':')) || null; + + if (callback) { callback(result); } + resolve(result); + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + }); + }); +} + +exports.baseboard = baseboard; + +function chassis(callback) { + const chassisTypes = ['Other', + 'Unknown', + 'Desktop', + 'Low Profile Desktop', + 'Pizza Box', + 'Mini Tower', + 'Tower', + 'Portable', + 'Laptop', + 'Notebook', + 'Hand Held', + 'Docking Station', + 'All in One', + 'Sub Notebook', + 'Space-Saving', + 'Lunch Box', + 'Main System Chassis', + 'Expansion Chassis', + 'SubChassis', + 'Bus Expansion Chassis', + 'Peripheral Chassis', + 'Storage Chassis', + 'Rack Mount Chassis', + 'Sealed-Case PC', + 'Multi-System Chassis', + 'Compact PCI', + 'Advanced TCA', + 'Blade', + 'Blade Enclosure', + 'Tablet', + 'Convertible', + 'Detachable', + 'IoT Gateway ', + 'Embedded PC', + 'Mini PC', + 'Stick PC', + ]; + + return new Promise((resolve) => { + process.nextTick(() => { + + let result = { + manufacturer: '', + model: '', + type: '', + version: '', + serial: '-', + assetTag: '-', + sku: '', + }; + if (_linux || _freebsd || _openbsd || _netbsd) { + const cmd = `echo -n "chassis_asset_tag: "; cat /sys/devices/virtual/dmi/id/chassis_asset_tag 2>/dev/null; echo; + echo -n "chassis_serial: "; cat /sys/devices/virtual/dmi/id/chassis_serial 2>/dev/null; echo; + echo -n "chassis_type: "; cat /sys/devices/virtual/dmi/id/chassis_type 2>/dev/null; echo; + echo -n "chassis_vendor: "; cat /sys/devices/virtual/dmi/id/chassis_vendor 2>/dev/null; echo; + echo -n "chassis_version: "; cat /sys/devices/virtual/dmi/id/chassis_version 2>/dev/null; echo;`; + exec(cmd, function (error, stdout) { + let lines = stdout.toString().split('\n'); + result.manufacturer = util.getValue(lines, 'chassis_vendor'); + const ctype = parseInt(util.getValue(lines, 'chassis_type').replace(/\D/g, '')); + result.type = (ctype && !isNaN(ctype) && ctype < chassisTypes.length) ? chassisTypes[ctype - 1] : ''; + result.version = util.getValue(lines, 'chassis_version'); + result.serial = util.getValue(lines, 'chassis_serial'); + result.assetTag = util.getValue(lines, 'chassis_asset_tag'); + if (result.manufacturer.toLowerCase().indexOf('o.e.m.') !== -1) { result.manufacturer = '-'; } + if (result.version.toLowerCase().indexOf('o.e.m.') !== -1) { result.version = '-'; } + if (result.serial.toLowerCase().indexOf('o.e.m.') !== -1) { result.serial = '-'; } + if (result.assetTag.toLowerCase().indexOf('o.e.m.') !== -1) { result.assetTag = '-'; } + + if (callback) { callback(result); } + resolve(result); + }); + } + if (_darwin) { + exec('ioreg -c IOPlatformExpertDevice -d 2', function (error, stdout) { + if (!error) { + let lines = stdout.toString().replace(/[<>"]/g, '').split('\n'); + result.manufacturer = util.getValue(lines, 'manufacturer', '=', true); + result.model = util.getValue(lines, 'model', '=', true); + result.version = util.getValue(lines, 'version', '=', true); + result.serial = util.getValue(lines, 'ioplatformserialnumber', '=', true); + result.assetTag = util.getValue(lines, 'board-id', '=', true); + } + + if (callback) { callback(result); } + resolve(result); + }); + } + if (_sunos) { + if (callback) { callback(result); } + resolve(result); + } + if (_windows) { + try { + util.powerShell('Get-WmiObject Win32_SystemEnclosure | select Model,Manufacturer,ChassisTypes,Version,SerialNumber,PartNumber,SKU | fl').then((stdout, error) => { + if (!error) { + let lines = stdout.toString().split('\r\n'); + + result.manufacturer = util.getValue(lines, 'manufacturer', ':'); + result.model = util.getValue(lines, 'model', ':'); + const ctype = parseInt(util.getValue(lines, 'ChassisTypes', ':').replace(/\D/g, '')); + result.type = (ctype && !isNaN(ctype) && ctype < chassisTypes.length) ? chassisTypes[ctype - 1] : ''; + result.version = util.getValue(lines, 'version', ':'); + result.serial = util.getValue(lines, 'serialnumber', ':'); + result.assetTag = util.getValue(lines, 'partnumber', ':'); + result.sku = util.getValue(lines, 'sku', ':'); + if (result.manufacturer.toLowerCase().indexOf('o.e.m.') !== -1) { result.manufacturer = '-'; } + if (result.version.toLowerCase().indexOf('o.e.m.') !== -1) { result.version = '-'; } + if (result.serial.toLowerCase().indexOf('o.e.m.') !== -1) { result.serial = '-'; } + if (result.assetTag.toLowerCase().indexOf('o.e.m.') !== -1) { result.assetTag = '-'; } + } + + if (callback) { callback(result); } + resolve(result); + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + }); + }); +} + +exports.chassis = chassis; + diff --git a/node_modules/systeminformation/lib/usb.js b/node_modules/systeminformation/lib/usb.js new file mode 100644 index 0000000..ef96f41 --- /dev/null +++ b/node_modules/systeminformation/lib/usb.js @@ -0,0 +1,305 @@ +'use strict'; +// @ts-check +// ================================================================================== +// usb.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 16. usb +// ---------------------------------------------------------------------------------- + +const exec = require('child_process').exec; +// const execSync = require('child_process').execSync; +const util = require('./util'); +// const fs = require('fs'); + +let _platform = process.platform; + +const _linux = (_platform === 'linux' || _platform === 'android'); +const _darwin = (_platform === 'darwin'); +const _windows = (_platform === 'win32'); +const _freebsd = (_platform === 'freebsd'); +const _openbsd = (_platform === 'openbsd'); +const _netbsd = (_platform === 'netbsd'); +const _sunos = (_platform === 'sunos'); + +function getLinuxUsbType(type, name) { + let result = type; + const str = (name + ' ' + type).toLowerCase(); + if (str.indexOf('camera') >= 0) { result = 'Camera'; } + else if (str.indexOf('hub') >= 0) { result = 'Hub'; } + else if (str.indexOf('keybrd') >= 0) { result = 'Keyboard'; } + else if (str.indexOf('keyboard') >= 0) { result = 'Keyboard'; } + else if (str.indexOf('mouse') >= 0) { result = 'Mouse'; } + else if (str.indexOf('stora') >= 0) { result = 'Storage'; } + else if (str.indexOf('mic') >= 0) { result = 'Microphone'; } + else if (str.indexOf('headset') >= 0) { result = 'Audio'; } + else if (str.indexOf('audio') >= 0) { result = 'Audio'; } + + return result; +} + +function parseLinuxUsb(usb) { + const result = {}; + const lines = usb.split('\n'); + if (lines && lines.length && lines[0].indexOf('Device') >= 0) { + const parts = lines[0].split(' '); + result.bus = parseInt(parts[0], 10); + if (parts[2]) { + result.deviceId = parseInt(parts[2], 10); + } else { + result.deviceId = null; + } + } else { + result.bus = null; + result.deviceId = null; + } + const idVendor = util.getValue(lines, 'idVendor', ' ', true).trim(); + let vendorParts = idVendor.split(' '); + vendorParts.shift(); + const vendor = vendorParts.join(' '); + + const idProduct = util.getValue(lines, 'idProduct', ' ', true).trim(); + let productParts = idProduct.split(' '); + productParts.shift(); + const product = productParts.join(' '); + + const interfaceClass = util.getValue(lines, 'bInterfaceClass', ' ', true).trim(); + let interfaceClassParts = interfaceClass.split(' '); + interfaceClassParts.shift(); + const usbType = interfaceClassParts.join(' '); + + const iManufacturer = util.getValue(lines, 'iManufacturer', ' ', true).trim(); + let iManufacturerParts = iManufacturer.split(' '); + iManufacturerParts.shift(); + const manufacturer = iManufacturerParts.join(' '); + + result.id = (idVendor.startsWith('0x') ? idVendor.split(' ')[0].substr(2, 10) : '') + ':' + (idProduct.startsWith('0x') ? idProduct.split(' ')[0].substr(2, 10) : ''); + result.name = product; + result.type = getLinuxUsbType(usbType, product); + result.removable = null; + result.vendor = vendor; + result.manufacturer = manufacturer; + result.maxPower = util.getValue(lines, 'MaxPower', ' ', true); + result.serialNumber = null; + + return result; +} + +// bus +// deviceId +// id +// name(product) +// type(bInterfaceClass) +// removable / hotplug +// vendor +// manufacturer +// maxpower(linux) + +function getDarwinUsbType(name) { + let result = ''; + if (name.indexOf('camera') >= 0) { result = 'Camera'; } + else if (name.indexOf('touch bar') >= 0) { result = 'Touch Bar'; } + else if (name.indexOf('controller') >= 0) { result = 'Controller'; } + else if (name.indexOf('headset') >= 0) { result = 'Audio'; } + else if (name.indexOf('keyboard') >= 0) { result = 'Keyboard'; } + else if (name.indexOf('trackpad') >= 0) { result = 'Trackpad'; } + else if (name.indexOf('sensor') >= 0) { result = 'Sensor'; } + else if (name.indexOf('bthusb') >= 0) { result = 'Bluetooth'; } + else if (name.indexOf('bth') >= 0) { result = 'Bluetooth'; } + else if (name.indexOf('rfcomm') >= 0) { result = 'Bluetooth'; } + else if (name.indexOf('usbhub') >= 0) { result = 'Hub'; } + else if (name.indexOf(' hub') >= 0) { result = 'Hub'; } + else if (name.indexOf('mouse') >= 0) { result = 'Mouse'; } + else if (name.indexOf('mic') >= 0) { result = 'Microphone'; } + else if (name.indexOf('removable') >= 0) { result = 'Storage'; } + return result; +} + + +function parseDarwinUsb(usb, id) { + const result = {}; + result.id = id; + + usb = usb.replace(/ \|/g, ''); + usb = usb.trim(); + let lines = usb.split('\n'); + lines.shift(); + try { + for (let i = 0; i < lines.length; i++) { + lines[i] = lines[i].trim(); + lines[i] = lines[i].replace(/=/g, ':'); + if (lines[i] !== '{' && lines[i] !== '}' && lines[i + 1] && lines[i + 1].trim() !== '}') { + lines[i] = lines[i] + ','; + } + lines[i] = lines[i].replace(': Yes,', ': "Yes",'); + lines[i] = lines[i].replace(': No,', ': "No",'); + } + const usbObj = JSON.parse(lines.join('\n')); + const removableDrive = usbObj['Built-In'].toLowerCase() !== 'yes' && usbObj['non-removable'].toLowerCase() === 'no'; + + result.bus = null; + result.deviceId = null; + result.id = usbObj['USB Address'] || null; + result.name = usbObj['kUSBProductString'] || usbObj['USB Product Name'] || null; + result.type = getDarwinUsbType((usbObj['kUSBProductString'] || usbObj['USB Product Name'] || '').toLowerCase() + (removableDrive ? ' removable' : '')); + result.removable = usbObj['non-removable'].toLowerCase() === 'no'; + result.vendor = usbObj['kUSBVendorString'] || usbObj['USB Vendor Name'] || null; + result.manufacturer = usbObj['kUSBVendorString'] || usbObj['USB Vendor Name'] || null; + result.maxPower = null; + result.serialNumber = usbObj['kUSBSerialNumberString'] || null; + + if (result.name) { + return result; + } else { + return null; + } + } catch (e) { + return null; + } +} + +// function getWindowsUsbType(service) { +// let result = '' +// if (service.indexOf('usbhub3') >= 0) { result = 'Hub'; } +// else if (service.indexOf('usbstor') >= 0) { result = 'Storage'; } +// else if (service.indexOf('hidusb') >= 0) { result = 'Input'; } +// else if (service.indexOf('usbccgp') >= 0) { result = 'Controller'; } +// else if (service.indexOf('usbxhci') >= 0) { result = 'Controller'; } +// else if (service.indexOf('usbehci') >= 0) { result = 'Controller'; } +// else if (service.indexOf('kbdhid') >= 0) { result = 'Keyboard'; } +// else if (service.indexOf('keyboard') >= 0) { result = 'Keyboard'; } +// else if (service.indexOf('pointing') >= 0) { result = 'Mouse'; } +// else if (service.indexOf('disk') >= 0) { result = 'Storage'; } +// else if (service.indexOf('usbhub') >= 0) { result = 'Hub'; } +// else if (service.indexOf('bthusb') >= 0) { result = ''; } +// else if (service.indexOf('bth') >= 0) { result = ''; } +// else if (service.indexOf('rfcomm') >= 0) { result = ''; } +// return result; +// } + +function getWindowsUsbTypeCreation(creationclass, name) { + let result = ''; + if (name.indexOf('storage') >= 0) { result = 'Storage'; } + else if (name.indexOf('speicher') >= 0) { result = 'Storage'; } + else if (creationclass.indexOf('usbhub') >= 0) { result = 'Hub'; } + else if (creationclass.indexOf('storage') >= 0) { result = 'Storage'; } + else if (creationclass.indexOf('usbcontroller') >= 0) { result = 'Controller'; } + else if (creationclass.indexOf('keyboard') >= 0) { result = 'Keyboard'; } + else if (creationclass.indexOf('pointing') >= 0) { result = 'Mouse'; } + else if (creationclass.indexOf('disk') >= 0) { result = 'Storage'; } + return result; +} + +function parseWindowsUsb(lines, id) { + const usbType = getWindowsUsbTypeCreation(util.getValue(lines, 'CreationClassName', ':').toLowerCase(), util.getValue(lines, 'name', ':').toLowerCase()); + + if (usbType) { + const result = {}; + result.bus = null; + result.deviceId = util.getValue(lines, 'deviceid', ':'); + result.id = id; + result.name = util.getValue(lines, 'name', ':'); + result.type = usbType; + result.removable = null; + result.vendor = null; + result.manufacturer = util.getValue(lines, 'Manufacturer', ':'); + result.maxPower = null; + result.serialNumber = null; + + return result; + } else { + return null; + } + +} + +function usb(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + let result = []; + if (_linux) { + const cmd = 'export LC_ALL=C; lsusb -v 2>/dev/null; unset LC_ALL'; + exec(cmd, { maxBuffer: 1024 * 1024 * 128 }, function (error, stdout) { + if (!error) { + const parts = ('\n\n' + stdout.toString()).split('\n\nBus '); + for (let i = 1; i < parts.length; i++) { + const usb = parseLinuxUsb(parts[i]); + result.push(usb); + } + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + if (_darwin) { + let cmd = 'ioreg -p IOUSB -c AppleUSBRootHubDevice -w0 -l'; + exec(cmd, { maxBuffer: 1024 * 1024 * 128 }, function (error, stdout) { + if (!error) { + const parts = (stdout.toString()).split(' +-o '); + for (let i = 1; i < parts.length; i++) { + const usb = parseDarwinUsb(parts[i]); + if (usb) { + result.push(usb); + } + } + if (callback) { + callback(result); + } + resolve(result); + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + if (_windows) { + util.powerShell('Get-WmiObject CIM_LogicalDevice | where { $_.Description -match "USB"} | select Name,CreationClassName,DeviceId,Manufacturer | fl').then((stdout, error) => { + if (!error) { + const parts = stdout.toString().split(/\n\s*\n/); + for (let i = 0; i < parts.length; i++) { + const usb = parseWindowsUsb(parts[i].split('\n'), i); + if (usb) { + result.push(usb); + } + } + } + if (callback) { + callback(result); + } + resolve(result); + }); + + // util.powerShell("gwmi Win32_USBControllerDevice |\%{[wmi]($_.Dependent)}").then(data => { + + // const parts = data.toString().split(/\n\s*\n/); + // for (let i = 0; i < parts.length; i++) { + // const usb = parseWindowsUsb(parts[i].split('\n'), i) + // if (usb) { + // result.push(usb) + // } + // } + // if (callback) { + // callback(result); + // } + // resolve(result); + // }); + } + if (_sunos || _freebsd || _openbsd || _netbsd) { + resolve(null); + } + }); + }); +} + +exports.usb = usb; + diff --git a/node_modules/systeminformation/lib/users.js b/node_modules/systeminformation/lib/users.js new file mode 100644 index 0000000..dee4c57 --- /dev/null +++ b/node_modules/systeminformation/lib/users.js @@ -0,0 +1,441 @@ +'use strict'; +// @ts-check +// ================================================================================== +// users.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 11. Users/Sessions +// ---------------------------------------------------------------------------------- + +const exec = require('child_process').exec; +const util = require('./util'); + +let _platform = process.platform; + +const _linux = (_platform === 'linux' || _platform === 'android'); +const _darwin = (_platform === 'darwin'); +const _windows = (_platform === 'win32'); +const _freebsd = (_platform === 'freebsd'); +const _openbsd = (_platform === 'openbsd'); +const _netbsd = (_platform === 'netbsd'); +const _sunos = (_platform === 'sunos'); + +// let _winDateFormat = { +// dateFormat: '', +// dateSeperator: '', +// timeFormat: '', +// timeSeperator: '', +// amDesignator: '', +// pmDesignator: '' +// }; + +// -------------------------- +// array of users online = sessions + +// function getWinCulture() { +// return new Promise((resolve) => { +// process.nextTick(() => { +// if (!_winDateFormat.dateFormat) { +// util.powerShell('(get-culture).DateTimeFormat') +// .then(data => { +// let lines = data.toString().split('\r\n'); +// _winDateFormat.dateFormat = util.getValue(lines, 'ShortDatePattern', ':'); +// _winDateFormat.dateSeperator = util.getValue(lines, 'DateSeparator', ':'); +// _winDateFormat.timeFormat = util.getValue(lines, 'ShortTimePattern', ':'); +// _winDateFormat.timeSeperator = util.getValue(lines, 'TimeSeparator', ':'); +// _winDateFormat.amDesignator = util.getValue(lines, 'AMDesignator', ':'); +// _winDateFormat.pmDesignator = util.getValue(lines, 'PMDesignator', ':'); + +// resolve(_winDateFormat); +// }) +// .catch(() => { +// resolve(_winDateFormat); +// }); +// } else { +// resolve(_winDateFormat); +// } +// }); +// }); +// } + +function parseUsersLinux(lines, phase) { + let result = []; + let result_who = []; + let result_w = {}; + let w_first = true; + let w_header = []; + let w_pos = []; + let who_line = {}; + + let is_whopart = true; + lines.forEach(function (line) { + if (line === '---') { + is_whopart = false; + } else { + let l = line.replace(/ +/g, ' ').split(' '); + + // who part + if (is_whopart) { + result_who.push({ + user: l[0], + tty: l[1], + date: l[2], + time: l[3], + ip: (l && l.length > 4) ? l[4].replace(/\(/g, '').replace(/\)/g, '') : '' + }); + } else { + // w part + if (w_first) { // header + w_header = l; + w_header.forEach(function (item) { + w_pos.push(line.indexOf(item)); + }); + w_first = false; + } else { + // split by w_pos + result_w.user = line.substring(w_pos[0], w_pos[1] - 1).trim(); + result_w.tty = line.substring(w_pos[1], w_pos[2] - 1).trim(); + result_w.ip = line.substring(w_pos[2], w_pos[3] - 1).replace(/\(/g, '').replace(/\)/g, '').trim(); + result_w.command = line.substring(w_pos[7], 1000).trim(); + // find corresponding 'who' line + who_line = result_who.filter(function (obj) { + return (obj.user.substring(0, 8).trim() === result_w.user && obj.tty === result_w.tty); + }); + if (who_line.length === 1) { + result.push({ + user: who_line[0].user, + tty: who_line[0].tty, + date: who_line[0].date, + time: who_line[0].time, + ip: who_line[0].ip, + command: result_w.command + }); + } + } + } + } + }); + if (result.length === 0 && phase === 2) { + return result_who; + } else { + return result; + } +} + +function parseUsersDarwin(lines) { + let result = []; + let result_who = []; + let result_w = {}; + let who_line = {}; + + let is_whopart = true; + lines.forEach(function (line) { + if (line === '---') { + is_whopart = false; + } else { + let l = line.replace(/ +/g, ' ').split(' '); + + // who part + if (is_whopart) { + result_who.push({ + user: l[0], + tty: l[1], + date: ('' + new Date().getFullYear()) + '-' + ('0' + ('JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC'.indexOf(l[2].toUpperCase()) / 3 + 1)).slice(-2) + '-' + ('0' + l[3]).slice(-2), + time: l[4], + }); + } else { + // w part + // split by w_pos + result_w.user = l[0]; + result_w.tty = l[1]; + result_w.ip = (l[2] !== '-') ? l[2] : ''; + result_w.command = l.slice(5, 1000).join(' '); + // find corresponding 'who' line + who_line = result_who.filter(function (obj) { + return (obj.user === result_w.user && (obj.tty.substring(3, 1000) === result_w.tty || obj.tty === result_w.tty)); + }); + if (who_line.length === 1) { + result.push({ + user: who_line[0].user, + tty: who_line[0].tty, + date: who_line[0].date, + time: who_line[0].time, + ip: result_w.ip, + command: result_w.command + }); + } + } + } + }); + return result; +} + +function users(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + let result = []; + + // linux + if (_linux) { + exec('who --ips; echo "---"; w | tail -n +2', function (error, stdout) { + if (!error) { + // lines / split + let lines = stdout.toString().split('\n'); + result = parseUsersLinux(lines, 1); + if (result.length === 0) { + exec('who; echo "---"; w | tail -n +2', function (error, stdout) { + if (!error) { + // lines / split + lines = stdout.toString().split('\n'); + result = parseUsersLinux(lines, 2); + } + if (callback) { callback(result); } + resolve(result); + }); + } else { + if (callback) { callback(result); } + resolve(result); + } + } else { + if (callback) { callback(result); } + resolve(result); + } + }); + } + if (_freebsd || _openbsd || _netbsd) { + exec('who; echo "---"; w -ih', function (error, stdout) { + if (!error) { + // lines / split + let lines = stdout.toString().split('\n'); + result = parseUsersDarwin(lines); + } + if (callback) { callback(result); } + resolve(result); + }); + } + if (_sunos) { + exec('who; echo "---"; w -h', function (error, stdout) { + if (!error) { + // lines / split + let lines = stdout.toString().split('\n'); + result = parseUsersDarwin(lines); + } + if (callback) { callback(result); } + resolve(result); + }); + } + + if (_darwin) { + exec('who; echo "---"; w -ih', function (error, stdout) { + if (!error) { + // lines / split + let lines = stdout.toString().split('\n'); + result = parseUsersDarwin(lines); + } + if (callback) { callback(result); } + resolve(result); + }); + } + if (_windows) { + try { + // const workload = []; + // // workload.push(util.powerShell('Get-CimInstance -ClassName Win32_Account | fl *')); + // workload.push(util.powerShell('Get-WmiObject Win32_LogonSession | fl *')); + // workload.push(util.powerShell('Get-WmiObject Win32_LoggedOnUser | fl *')); + // workload.push(util.powerShell('Get-WmiObject Win32_Process -Filter "name=\'explorer.exe\'" | Select @{Name="domain";Expression={$_.GetOwner().Domain}}, @{Name="username";Expression={$_.GetOwner().User}} | fl')); + // Promise.all( + // workload + // ).then(data => { + let cmd = 'Get-WmiObject Win32_LogonSession | select LogonId,StartTime | fl' + '; echo \'#-#-#-#\';'; + cmd += 'Get-WmiObject Win32_LoggedOnUser | select antecedent,dependent | fl ' + '; echo \'#-#-#-#\';'; + cmd += 'Get-WmiObject Win32_Process -Filter "name=\'explorer.exe\'" | Select @{Name="sessionid";Expression={$_.SessionId}}, @{Name="domain";Expression={$_.GetOwner().Domain}}, @{Name="username";Expression={$_.GetOwner().User}} | fl' + '; echo \'#-#-#-#\';'; + cmd += 'query user'; + util.powerShell(cmd).then(data => { + // controller + vram + // let accounts = parseWinAccounts(data[0].split(/\n\s*\n/)); + if (data) { + data = data.split('#-#-#-#'); + let sessions = parseWinSessions((data[0] || '').split(/\n\s*\n/)); + let loggedons = parseWinLoggedOn((data[1] || '').split(/\n\s*\n/)); + let queryUser = parseWinUsersQuery((data[3] || '').split('\r\n')); + let users = parseWinUsers((data[2] || '').split(/\n\s*\n/), queryUser); + for (let id in loggedons) { + if ({}.hasOwnProperty.call(loggedons, id)) { + loggedons[id].dateTime = {}.hasOwnProperty.call(sessions, id) ? sessions[id] : ''; + } + } + users.forEach(user => { + let dateTime = ''; + for (let id in loggedons) { + if ({}.hasOwnProperty.call(loggedons, id)) { + if (loggedons[id].user === user.user && (!dateTime || dateTime < loggedons[id].dateTime)) { + dateTime = loggedons[id].dateTime; + } + } + } + + result.push({ + user: user.user, + tty: user.tty, + date: `${dateTime.substr(0, 4)}-${dateTime.substr(4, 2)}-${dateTime.substr(6, 2)}`, + time: `${dateTime.substr(8, 2)}:${dateTime.substr(10, 2)}`, + ip: '', + command: '' + }); + }); + } + if (callback) { callback(result); } + resolve(result); + + }); + // util.powerShell('query user').then(stdout => { + // if (stdout) { + // // lines / split + // let lines = stdout.toString().split('\r\n'); + // getWinCulture() + // .then(culture => { + // result = parseUsersWin(lines, culture); + // if (callback) { callback(result); } + // resolve(result); + // }); + // } else { + // if (callback) { callback(result); } + // resolve(result); + // } + // }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + + }); + }); +} + +// function parseWinAccounts(accountParts) { +// const accounts = []; +// accountParts.forEach(account => { +// const lines = account.split('\r\n'); +// const name = util.getValue(lines, 'name', ':', true); +// const domain = util.getValue(lines, 'domain', ':', true); +// accounts.push(`${domain}\${name}`); +// }); +// return accounts; +// } + +function parseWinSessions(sessionParts) { + const sessions = {}; + sessionParts.forEach(session => { + const lines = session.split('\r\n'); + const id = util.getValue(lines, 'LogonId'); + const starttime = util.getValue(lines, 'starttime'); + if (id) { + sessions[id] = starttime; + } + }); + return sessions; +} + +function fuzzyMatch(name1, name2) { + name1 = name1.toLowerCase(); + name2 = name2.toLowerCase(); + let eq = 0; + let len = name1.length; + if (name2.length > len) { len = name2.length; } + + for (let i = 0; i < len; i++) { + const c1 = name1[i] || ''; + const c2 = name2[i] || ''; + if (c1 === c2) { eq++; } + } + return (len > 10 ? eq / len > 0.9 : (len > 0 ? eq / len > 0.8 : false)); +} + +function parseWinUsers(userParts, userQuery) { + const users = []; + userParts.forEach(user => { + const lines = user.split('\r\n'); + + const domain = util.getValue(lines, 'domain', ':', true); + const username = util.getValue(lines, 'username', ':', true); + const sessionid = util.getValue(lines, 'sessionid', ':', true); + + if (username) { + const quser = userQuery.filter(item => fuzzyMatch(item.user, username)); + users.push({ + domain, + user: username, + tty: quser && quser[0] && quser[0].tty ? quser[0].tty : sessionid + }); + } + }); + return users; +} + +function parseWinLoggedOn(loggedonParts) { + const loggedons = {}; + loggedonParts.forEach(loggedon => { + const lines = loggedon.split('\r\n'); + + const antecendent = util.getValue(lines, 'antecedent', ':', true); + let parts = antecendent.split(','); + const domainParts = parts.length > 1 ? parts[0].split('=') : []; + const nameParts = parts.length > 1 ? parts[1].split('=') : []; + const domain = domainParts.length > 1 ? domainParts[1].replace(/"/g, '') : ''; + const name = nameParts.length > 1 ? nameParts[1].replace(/"/g, '') : ''; + const dependent = util.getValue(lines, 'dependent', ':', true); + parts = dependent.split('='); + const id = parts.length > 1 ? parts[1].replace(/"/g, '') : ''; + if (id) { + loggedons[id] = { + domain, + user: name + }; + } + }); + return loggedons; +} + +function parseWinUsersQuery(lines) { + lines = lines.filter(item => item); + let result = []; + const header = lines[0]; + const headerDelimiter = []; + if (header) { + const start = (header[0] === ' ') ? 1 : 0; + headerDelimiter.push(start - 1); + let nextSpace = 0; + for (let i = start + 1; i < header.length; i++) { + if (header[i] === ' ' && ((header[i - 1] === ' ') || (header[i - 1] === '.'))) { + nextSpace = i; + } else { + if (nextSpace) { + headerDelimiter.push(nextSpace); + nextSpace = 0; + } + } + } + for (let i = 1; i < lines.length; i++) { + if (lines[i].trim()) { + const user = lines[i].substring(headerDelimiter[0] + 1, headerDelimiter[1]).trim() || ''; + const tty = lines[i].substring(headerDelimiter[1] + 1, headerDelimiter[2] - 2).trim() || ''; + // const dateTime = util.parseDateTime(lines[i].substring(headerDelimiter[5] + 1, 2000).trim(), culture) || ''; + result.push({ + user: user, + tty: tty, + }); + } + } + } + return result; +} + +exports.users = users; diff --git a/node_modules/systeminformation/lib/util.js b/node_modules/systeminformation/lib/util.js new file mode 100644 index 0000000..6232022 --- /dev/null +++ b/node_modules/systeminformation/lib/util.js @@ -0,0 +1,1276 @@ +'use strict'; +// @ts-check +// ================================================================================== +// utils.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 0. helper functions +// ---------------------------------------------------------------------------------- + +const os = require('os'); +const fs = require('fs'); +const path = require('path'); +const spawn = require('child_process').spawn; +const exec = require('child_process').exec; +const execSync = require('child_process').execSync; +const util = require('util'); + +let _platform = process.platform; +const _linux = (_platform === 'linux' || _platform === 'android'); +const _darwin = (_platform === 'darwin'); +const _windows = (_platform === 'win32'); +const _freebsd = (_platform === 'freebsd'); +const _openbsd = (_platform === 'openbsd'); +const _netbsd = (_platform === 'netbsd'); +// const _sunos = (_platform === 'sunos'); + +let _cores = 0; +let wmicPath = ''; +let codepage = ''; +let _smartMonToolsInstalled = null; + +const WINDIR = process.env.WINDIR || 'C:\\Windows'; + +// powerShell +let _psChild; +let _psResult = ''; +let _psCmds = []; +let _psPersistent = false; +const _psToUTF8 = '$OutputEncoding = [System.Console]::OutputEncoding = [System.Console]::InputEncoding = [System.Text.Encoding]::UTF8 ; '; +const _psCmdStart = '--###START###--'; +const _psError = '--ERROR--'; +const _psCmdSeperator = '--###ENDCMD###--'; +const _psIdSeperator = '--##ID##--'; + +const execOptsWin = { + windowsHide: true, + maxBuffer: 1024 * 20000, + encoding: 'UTF-8', + env: util._extend({}, process.env, { LANG: 'en_US.UTF-8' }) +}; + +function toInt(value) { + let result = parseInt(value, 10); + if (isNaN(result)) { + result = 0; + } + return result; +} + + +const stringReplace = new String().replace; +const stringToLower = new String().toLowerCase; +const stringToString = new String().toString; +const stringSubstr = new String().substr; +const stringTrim = new String().trim; +const stringStartWith = new String().startsWith; +const mathMin = Math.min; + +function isFunction(functionToCheck) { + let getType = {}; + return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; +} + +function unique(obj) { + let uniques = []; + let stringify = {}; + for (let i = 0; i < obj.length; i++) { + let keys = Object.keys(obj[i]); + keys.sort(function (a, b) { return a - b; }); + let str = ''; + for (let j = 0; j < keys.length; j++) { + str += JSON.stringify(keys[j]); + str += JSON.stringify(obj[i][keys[j]]); + } + if (!{}.hasOwnProperty.call(stringify, str)) { + uniques.push(obj[i]); + stringify[str] = true; + } + } + return uniques; +} + +function sortByKey(array, keys) { + return array.sort(function (a, b) { + let x = ''; + let y = ''; + keys.forEach(function (key) { + x = x + a[key]; y = y + b[key]; + }); + return ((x < y) ? -1 : ((x > y) ? 1 : 0)); + }); +} + +function cores() { + if (_cores === 0) { + _cores = os.cpus().length; + } + return _cores; +} + +function getValue(lines, property, separator, trimmed, lineMatch) { + separator = separator || ':'; + property = property.toLowerCase(); + trimmed = trimmed || false; + lineMatch = lineMatch || false; + for (let i = 0; i < lines.length; i++) { + let line = lines[i].toLowerCase().replace(/\t/g, ''); + if (trimmed) { + line = line.trim(); + } + if (line.startsWith(property) && (lineMatch ? (line.match(property + separator)) : true)) { + const parts = trimmed ? lines[i].trim().split(separator) : lines[i].split(separator); + if (parts.length >= 2) { + parts.shift(); + return parts.join(separator).trim(); + } else { + return ''; + } + } + } + return ''; +} + +function decodeEscapeSequence(str, base) { + base = base || 16; + return str.replace(/\\x([0-9A-Fa-f]{2})/g, function () { + return String.fromCharCode(parseInt(arguments[1], base)); + }); +} + +function detectSplit(str) { + let seperator = ''; + let part = 0; + str.split('').forEach(element => { + if (element >= '0' && element <= '9') { + if (part === 1) { part++; } + } else { + if (part === 0) { part++; } + if (part === 1) { + seperator += element; + } + } + }); + return seperator; +} + +function parseTime(t, pmDesignator) { + pmDesignator = pmDesignator || ''; + t = t.toUpperCase(); + let hour = 0; + let min = 0; + let splitter = detectSplit(t); + let parts = t.split(splitter); + if (parts.length >= 2) { + if (parts[2]) { + parts[1] += parts[2]; + } + let isPM = (parts[1] && (parts[1].toLowerCase().indexOf('pm') > -1) || (parts[1].toLowerCase().indexOf('p.m.') > -1) || (parts[1].toLowerCase().indexOf('p. m.') > -1) || (parts[1].toLowerCase().indexOf('n') > -1) || (parts[1].toLowerCase().indexOf('ch') > -1) || (parts[1].toLowerCase().indexOf('ös') > -1) || (pmDesignator && parts[1].toLowerCase().indexOf(pmDesignator) > -1)); + hour = parseInt(parts[0], 10); + min = parseInt(parts[1], 10); + hour = isPM && hour < 12 ? hour + 12 : hour; + return ('0' + hour).substr(-2) + ':' + ('0' + min).substr(-2); + } +} + +function parseDateTime(dt, culture) { + const result = { + date: '', + time: '' + }; + culture = culture || {}; + let dateFormat = (culture.dateFormat || '').toLowerCase(); + let pmDesignator = (culture.pmDesignator || ''); + + const parts = dt.split(' '); + if (parts[0]) { + if (parts[0].indexOf('/') >= 0) { + // Dateformat: mm/dd/yyyy or dd/mm/yyyy or dd/mm/yy or yyyy/mm/dd + const dtparts = parts[0].split('/'); + if (dtparts.length === 3) { + if (dtparts[0].length === 4) { + // Dateformat: yyyy/mm/dd + result.date = dtparts[0] + '-' + ('0' + dtparts[1]).substr(-2) + '-' + ('0' + dtparts[2]).substr(-2); + } else if (dtparts[2].length === 2) { + if ((dateFormat.indexOf('/d/') > -1 || dateFormat.indexOf('/dd/') > -1)) { + // Dateformat: mm/dd/yy + result.date = '20' + dtparts[2] + '-' + ('0' + dtparts[1]).substr(-2) + '-' + ('0' + dtparts[0]).substr(-2); + } else { + // Dateformat: dd/mm/yy + result.date = '20' + dtparts[2] + '-' + ('0' + dtparts[1]).substr(-2) + '-' + ('0' + dtparts[0]).substr(-2); + } + } else { + // Dateformat: mm/dd/yyyy or dd/mm/yyyy + const isEN = ((dt.toLowerCase().indexOf('pm') > -1) || (dt.toLowerCase().indexOf('p.m.') > -1) || (dt.toLowerCase().indexOf('p. m.') > -1) || (dt.toLowerCase().indexOf('am') > -1) || (dt.toLowerCase().indexOf('a.m.') > -1) || (dt.toLowerCase().indexOf('a. m.') > -1)); + if ((isEN || dateFormat.indexOf('/d/') > -1 || dateFormat.indexOf('/dd/') > -1) && dateFormat.indexOf('dd/') !== 0) { + // Dateformat: mm/dd/yyyy + result.date = dtparts[2] + '-' + ('0' + dtparts[0]).substr(-2) + '-' + ('0' + dtparts[1]).substr(-2); + } else { + // Dateformat: dd/mm/yyyy + result.date = dtparts[2] + '-' + ('0' + dtparts[1]).substr(-2) + '-' + ('0' + dtparts[0]).substr(-2); + } + } + } + } + if (parts[0].indexOf('.') >= 0) { + const dtparts = parts[0].split('.'); + if (dtparts.length === 3) { + if (dateFormat.indexOf('.d.') > -1 || dateFormat.indexOf('.dd.') > -1) { + // Dateformat: mm.dd.yyyy + result.date = dtparts[2] + '-' + ('0' + dtparts[0]).substr(-2) + '-' + ('0' + dtparts[1]).substr(-2); + } else { + // Dateformat: dd.mm.yyyy + result.date = dtparts[2] + '-' + ('0' + dtparts[1]).substr(-2) + '-' + ('0' + dtparts[0]).substr(-2); + } + } + } + if (parts[0].indexOf('-') >= 0) { + // Dateformat: yyyy-mm-dd + const dtparts = parts[0].split('-'); + if (dtparts.length === 3) { + result.date = dtparts[0] + '-' + ('0' + dtparts[1]).substr(-2) + '-' + ('0' + dtparts[2]).substr(-2); + } + } + } + if (parts[1]) { + parts.shift(); + let time = parts.join(' '); + result.time = parseTime(time, pmDesignator); + } + return result; +} + +function parseHead(head, rights) { + let space = (rights > 0); + let count = 1; + let from = 0; + let to = 0; + let result = []; + for (let i = 0; i < head.length; i++) { + if (count <= rights) { + // if (head[i] === ' ' && !space) { + if (/\s/.test(head[i]) && !space) { + to = i - 1; + result.push({ + from: from, + to: to + 1, + cap: head.substring(from, to + 1) + }); + from = to + 2; + count++; + } + space = head[i] === ' '; + } else { + if (!/\s/.test(head[i]) && space) { + to = i - 1; + if (from < to) { + result.push({ + from: from, + to: to, + cap: head.substring(from, to) + }); + } + from = to + 1; + count++; + } + space = head[i] === ' '; + } + } + to = 1000; + result.push({ + from: from, + to: to, + cap: head.substring(from, to) + }); + let len = result.length; + for (var i = 0; i < len; i++) { + if (result[i].cap.replace(/\s/g, '').length === 0) { + if (i + 1 < len) { + result[i].to = result[i + 1].to; + result[i].cap = result[i].cap + result[i + 1].cap; + result.splice(i + 1, 1); + len = len - 1; + } + } + } + return result; +} + +function findObjectByKey(array, key, value) { + for (let i = 0; i < array.length; i++) { + if (array[i][key] === value) { + return i; + } + } + return -1; +} + +function getWmic() { + if (os.type() === 'Windows_NT' && !wmicPath) { + wmicPath = WINDIR + '\\system32\\wbem\\wmic.exe'; + if (!fs.existsSync(wmicPath)) { + try { + const wmicPathArray = execSync('WHERE WMIC', execOptsWin).toString().split('\r\n'); + if (wmicPathArray && wmicPathArray.length) { + wmicPath = wmicPathArray[0]; + } else { + wmicPath = 'wmic'; + } + } catch (e) { + wmicPath = 'wmic'; + } + } + } + return wmicPath; +} + +function wmic(command) { + return new Promise((resolve) => { + process.nextTick(() => { + try { + powerShell(getWmic() + ' ' + command).then(stdout => { + resolve(stdout, ''); + }); + } catch (e) { + resolve('', e); + } + }); + }); +} + +// function wmic(command, options) { +// options = options || execOptsWin; +// return new Promise((resolve) => { +// process.nextTick(() => { +// try { +// exec(WINDIR + '\\system32\\chcp.com 65001 | ' + getWmic() + ' ' + command, options, function (error, stdout) { +// resolve(stdout, error); +// }).stdin.end(); +// } catch (e) { +// resolve('', e); +// } +// }); +// }); +// } + +function getVboxmanage() { + return _windows ? `"${process.env.VBOX_INSTALL_PATH || process.env.VBOX_MSI_INSTALL_PATH}\\VBoxManage.exe"` : 'vboxmanage'; +} + +function powerShellProceedResults(data) { + let id = ''; + let parts; + let res = ''; + // startID + if (data.indexOf(_psCmdStart) >= 0) { + parts = data.split(_psCmdStart); + const parts2 = parts[1].split(_psIdSeperator); + id = parts2[0]; + if (parts2.length > 1) { + data = parts2.slice(1).join(_psIdSeperator); + } + } + // result; + if (data.indexOf(_psCmdSeperator) >= 0) { + parts = data.split(_psCmdSeperator); + res = parts[0]; + } + let remove = -1; + for (let i = 0; i < _psCmds.length; i++) { + if (_psCmds[i].id === id) { + remove = i; + // console.log(`----- TIME : ${(new Date() - _psCmds[i].start) * 0.001} s`); + + _psCmds[i].callback(res); + } + } + if (remove >= 0) { + _psCmds.splice(remove, 1); + } +} + +function powerShellStart() { + _psChild = spawn('powershell.exe', ['-NoLogo', '-InputFormat', 'Text', '-NoExit', '-Command', '-'], { + stdio: 'pipe', + windowsHide: true, + maxBuffer: 1024 * 20000, + encoding: 'UTF-8', + env: util._extend({}, process.env, { LANG: 'en_US.UTF-8' }) + }); + if (_psChild && _psChild.pid) { + _psPersistent = true; + _psChild.stdout.on('data', function (data) { + _psResult = _psResult + data.toString('utf8'); + if (data.indexOf(_psCmdSeperator) >= 0) { + powerShellProceedResults(_psResult); + _psResult = ''; + } + }); + _psChild.stderr.on('data', function () { + powerShellProceedResults(_psResult + _psError); + }); + _psChild.on('error', function () { + powerShellProceedResults(_psResult + _psError); + }); + _psChild.on('close', function () { + _psChild.kill(); + }); + } +} + +function powerShellRelease() { + try { + _psChild.stdin.write('exit' + os.EOL); + _psChild.stdin.end(); + _psPersistent = false; + } catch (e) { + _psChild.kill(); + } +} + +function powerShell(cmd) { + + if (_psPersistent) { + const id = Math.random().toString(36).substr(2, 10); + return new Promise((resolve) => { + process.nextTick(() => { + function callback(data) { + resolve(data); + } + _psCmds.push({ + id, + cmd, + callback, + start: new Date() + }); + try { + if (_psChild && _psChild.pid) { + _psChild.stdin.write(_psToUTF8 + 'echo ' + _psCmdStart + id + _psIdSeperator + '; ' + os.EOL + cmd + os.EOL + 'echo ' + _psCmdSeperator + os.EOL); + } + } catch (e) { + resolve(''); + } + }); + }); + + } else { + let result = ''; + + return new Promise((resolve) => { + process.nextTick(() => { + try { + // const start = new Date(); + const child = spawn('powershell.exe', ['-NoLogo', '-InputFormat', 'Text', '-NoExit', '-ExecutionPolicy', 'Unrestricted', '-Command', '-'], { + stdio: 'pipe', + windowsHide: true, + maxBuffer: 1024 * 20000, + encoding: 'UTF-8', + env: util._extend({}, process.env, { LANG: 'en_US.UTF-8' }) + }); + + if (child && !child.pid) { + child.on('error', function () { + resolve(result); + }); + } + if (child && child.pid) { + child.stdout.on('data', function (data) { + result = result + data.toString('utf8'); + }); + child.stderr.on('data', function () { + child.kill(); + resolve(result); + }); + child.on('close', function () { + child.kill(); + // console.log(`----- TIME : ${(new Date() - start) * 0.001} s`); + + resolve(result); + }); + child.on('error', function () { + child.kill(); + resolve(result); + }); + try { + child.stdin.write(_psToUTF8 + cmd + os.EOL); + child.stdin.write('exit' + os.EOL); + child.stdin.end(); + } catch (e) { + child.kill(); + resolve(result); + } + } else { + resolve(result); + } + } catch (e) { + resolve(result); + } + }); + }); + } +} + +function execSafe(cmd, args, options) { + let result = ''; + options = options || {}; + + return new Promise((resolve) => { + process.nextTick(() => { + try { + const child = spawn(cmd, args, options); + + if (child && !child.pid) { + child.on('error', function () { + resolve(result); + }); + } + if (child && child.pid) { + child.stdout.on('data', function (data) { + result += data.toString(); + }); + child.on('close', function () { + child.kill(); + resolve(result); + }); + child.on('error', function () { + child.kill(); + resolve(result); + }); + } else { + resolve(result); + } + } catch (e) { + resolve(result); + } + }); + }); +} + +function getCodepage() { + if (_windows) { + if (!codepage) { + try { + const stdout = execSync('chcp', execOptsWin); + const lines = stdout.toString().split('\r\n'); + const parts = lines[0].split(':'); + codepage = parts.length > 1 ? parts[1].replace('.', '') : ''; + } catch (err) { + codepage = '437'; + } + } + return codepage; + } + if (_linux || _darwin || _freebsd || _openbsd || _netbsd) { + if (!codepage) { + try { + const stdout = execSync('echo $LANG'); + const lines = stdout.toString().split('\r\n'); + const parts = lines[0].split('.'); + codepage = parts.length > 1 ? parts[1].trim() : ''; + if (!codepage) { + codepage = 'UTF-8'; + } + } catch (err) { + codepage = 'UTF-8'; + } + } + return codepage; + } +} + +function smartMonToolsInstalled() { + if (_smartMonToolsInstalled !== null) { + return _smartMonToolsInstalled; + } + _smartMonToolsInstalled = false; + if (_windows) { + try { + const pathArray = execSync('WHERE smartctl 2>nul', execOptsWin).toString().split('\r\n'); + if (pathArray && pathArray.length) { + _smartMonToolsInstalled = pathArray[0].indexOf(':\\') >= 0; + } else { + _smartMonToolsInstalled = false; + } + } catch (e) { + _smartMonToolsInstalled = false; + } + } + if (_linux || _darwin || _freebsd || _openbsd || _netbsd) { + const pathArray = execSync('which smartctl 2>/dev/null', execOptsWin).toString().split('\r\n'); + _smartMonToolsInstalled = pathArray.length > 0; + } + return _smartMonToolsInstalled; +} + +function isRaspberry() { + const PI_MODEL_NO = [ + 'BCM2708', + 'BCM2709', + 'BCM2710', + 'BCM2711', + 'BCM2835', + 'BCM2836', + 'BCM2837', + 'BCM2837B0' + ]; + let cpuinfo = []; + try { + cpuinfo = fs.readFileSync('/proc/cpuinfo', { encoding: 'utf8' }).toString().split('\n'); + } catch (e) { + return false; + } + const hardware = getValue(cpuinfo, 'hardware'); + return (hardware && PI_MODEL_NO.indexOf(hardware) > -1); +} + +function isRaspbian() { + let osrelease = []; + try { + osrelease = fs.readFileSync('/etc/os-release', { encoding: 'utf8' }).toString().split('\n'); + } catch (e) { + return false; + } + const id = getValue(osrelease, 'id', '='); + return (id && id.indexOf('raspbian') > -1); +} + +function execWin(cmd, opts, callback) { + if (!callback) { + callback = opts; + opts = execOptsWin; + } + let newCmd = 'chcp 65001 > nul && cmd /C ' + cmd + ' && chcp ' + codepage + ' > nul'; + exec(newCmd, opts, function (error, stdout) { + callback(error, stdout); + }); +} + +function darwinXcodeExists() { + const cmdLineToolsExists = fs.existsSync('/Library/Developer/CommandLineTools/usr/bin/'); + const xcodeAppExists = fs.existsSync('/Applications/Xcode.app/Contents/Developer/Tools'); + const xcodeExists = fs.existsSync('/Library/Developer/Xcode/'); + return (cmdLineToolsExists || xcodeExists || xcodeAppExists); +} + +function nanoSeconds() { + const time = process.hrtime(); + if (!Array.isArray(time) || time.length !== 2) { + return 0; + } + return +time[0] * 1e9 + +time[1]; +} + +function countUniqueLines(lines, startingWith) { + startingWith = startingWith || ''; + const uniqueLines = []; + lines.forEach(line => { + if (line.startsWith(startingWith)) { + if (uniqueLines.indexOf(line) === -1) { + uniqueLines.push(line); + } + } + }); + return uniqueLines.length; +} + +function countLines(lines, startingWith) { + startingWith = startingWith || ''; + const uniqueLines = []; + lines.forEach(line => { + if (line.startsWith(startingWith)) { + uniqueLines.push(line); + } + }); + return uniqueLines.length; +} + +function sanitizeShellString(str, strict) { + if (typeof strict === 'undefined') { strict = false; } + const s = str || ''; + let result = ''; + for (let i = 0; i <= mathMin(s.length, 2000); i++) { + if (!(s[i] === undefined || + s[i] === '>' || + s[i] === '<' || + s[i] === '*' || + s[i] === '?' || + s[i] === '[' || + s[i] === ']' || + s[i] === '|' || + s[i] === '˚' || + s[i] === '$' || + s[i] === ';' || + s[i] === '&' || + s[i] === '(' || + s[i] === ')' || + s[i] === ']' || + s[i] === '#' || + s[i] === '\\' || + s[i] === '\t' || + s[i] === '\n' || + s[i] === '\'' || + s[i] === '`' || + s[i] === '"' || + s[i].length > 1 || + (strict && s[i] === '@') || + (strict && s[i] === ' ') || + (strict && s[i] == '{') || + (strict && s[i] == ')'))) { + result = result + s[i]; + } + } + return result; +} + +function isPrototypePolluted() { + const s = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + let notPolluted = true; + let st = ''; + + st.__proto__.replace = stringReplace; + st.__proto__.toLowerCase = stringToLower; + st.__proto__.toString = stringToString; + st.__proto__.substr = stringSubstr; + + notPolluted = notPolluted || !(s.length === 62); + const ms = Date.now(); + if (typeof ms === 'number' && ms > 1600000000000) { + const l = ms % 100 + 15; + for (let i = 0; i < l; i++) { + const r = Math.random() * 61.99999999 + 1; + const rs = parseInt(Math.floor(r).toString(), 10); + const rs2 = parseInt(r.toString().split('.')[0], 10); + const q = Math.random() * 61.99999999 + 1; + const qs = parseInt(Math.floor(q).toString(), 10); + const qs2 = parseInt(q.toString().split('.')[0], 10); + notPolluted = notPolluted && !(r === q); + notPolluted = notPolluted && rs === rs2 && qs === qs2; + st += s[rs - 1]; + } + notPolluted = notPolluted && st.length === l; + // string manipulation + let p = Math.random() * l * 0.9999999999; + let stm = st.substr(0, p) + ' ' + st.substr(p, 2000); + stm.__proto__.replace = stringReplace; + let sto = stm.replace(/ /g, ''); + notPolluted = notPolluted && st === sto; + p = Math.random() * l * 0.9999999999; + stm = st.substr(0, p) + '{' + st.substr(p, 2000); + sto = stm.replace(/{/g, ''); + notPolluted = notPolluted && st === sto; + p = Math.random() * l * 0.9999999999; + stm = st.substr(0, p) + '*' + st.substr(p, 2000); + sto = stm.replace(/\*/g, ''); + notPolluted = notPolluted && st === sto; + p = Math.random() * l * 0.9999999999; + stm = st.substr(0, p) + '$' + st.substr(p, 2000); + sto = stm.replace(/\$/g, ''); + notPolluted = notPolluted && st === sto; + + // lower + const stl = st.toLowerCase(); + notPolluted = notPolluted && (stl.length === l) && stl[l - 1] && !(stl[l]); + for (let i = 0; i < l; i++) { + const s1 = st[i]; + s1.__proto__.toLowerCase = stringToLower; + const s2 = stl ? stl[i] : ''; + const s1l = s1.toLowerCase(); + notPolluted = notPolluted && s1l[0] === s2 && s1l[0] && !(s1l[1]); + } + } + return !notPolluted; +} + +function hex2bin(hex) { + return ('00000000' + (parseInt(hex, 16)).toString(2)).substr(-8); +} + +function getFilesInPath(source) { + const lstatSync = fs.lstatSync; + const readdirSync = fs.readdirSync; + const join = path.join; + + function isDirectory(source) { + return lstatSync(source).isDirectory(); + } + function isFile(source) { return lstatSync(source).isFile(); } + + function getDirectories(source) { + return readdirSync(source).map(function (name) { return join(source, name); }).filter(isDirectory); + } + function getFiles(source) { + return readdirSync(source).map(function (name) { return join(source, name); }).filter(isFile); + } + + function getFilesRecursively(source) { + try { + let dirs = getDirectories(source); + let files = dirs + .map(function (dir) { return getFilesRecursively(dir); }) + .reduce(function (a, b) { return a.concat(b); }, []); + return files.concat(getFiles(source)); + } catch (e) { + return []; + } + } + + if (fs.existsSync(source)) { + return getFilesRecursively(source); + } else { + return []; + } +} + +function decodePiCpuinfo(lines) { + + // https://www.raspberrypi.org/documentation/hardware/raspberrypi/revision-codes/README.md + + const oldRevisionCodes = { + '0002': { + type: 'B', + revision: '1.0', + memory: 256, + manufacturer: 'Egoman', + processor: 'BCM2835' + }, + '0003': { + type: 'B', + revision: '1.0', + memory: 256, + manufacturer: 'Egoman', + processor: 'BCM2835' + }, + '0004': { + type: 'B', + revision: '2.0', + memory: 256, + manufacturer: 'Sony UK', + processor: 'BCM2835' + }, + '0005': { + type: 'B', + revision: '2.0', + memory: 256, + manufacturer: 'Qisda', + processor: 'BCM2835' + }, + '0006': { + type: 'B', + revision: '2.0', + memory: 256, + manufacturer: 'Egoman', + processor: 'BCM2835' + }, + '0007': { + type: 'A', + revision: '2.0', + memory: 256, + manufacturer: 'Egoman', + processor: 'BCM2835' + }, + '0008': { + type: 'A', + revision: '2.0', + memory: 256, + manufacturer: 'Sony UK', + processor: 'BCM2835' + }, + '0009': { + type: 'A', + revision: '2.0', + memory: 256, + manufacturer: 'Qisda', + processor: 'BCM2835' + }, + '000d': { + type: 'B', + revision: '2.0', + memory: 512, + manufacturer: 'Egoman', + processor: 'BCM2835' + }, + '000e': { + type: 'B', + revision: '2.0', + memory: 512, + manufacturer: 'Sony UK', + processor: 'BCM2835' + }, + '000f': { + type: 'B', + revision: '2.0', + memory: 512, + manufacturer: 'Egoman', + processor: 'BCM2835' + }, + '0010': { + type: 'B+', + revision: '1.2', + memory: 512, + manufacturer: 'Sony UK', + processor: 'BCM2835' + }, + '0011': { + type: 'CM1', + revision: '1.0', + memory: 512, + manufacturer: 'Sony UK', + processor: 'BCM2835' + }, + '0012': { + type: 'A+', + revision: '1.1', + memory: 256, + manufacturer: 'Sony UK', + processor: 'BCM2835' + }, + '0013': { + type: 'B+', + revision: '1.2', + memory: 512, + manufacturer: 'Embest', + processor: 'BCM2835' + }, + '0014': { + type: 'CM1', + revision: '1.0', + memory: 512, + manufacturer: 'Embest', + processor: 'BCM2835' + }, + '0015': { + type: 'A+', + revision: '1.1', + memory: 256, + manufacturer: '512MB Embest', + processor: 'BCM2835' + } + }; + + const processorList = [ + 'BCM2835', + 'BCM2836', + 'BCM2837', + 'BCM2711', + ]; + const manufacturerList = [ + 'Sony UK', + 'Egoman', + 'Embest', + 'Sony Japan', + 'Embest', + 'Stadium' + ]; + const typeList = { + '00': 'A', + '01': 'B', + '02': 'A+', + '03': 'B+', + '04': '2B', + '05': 'Alpha (early prototype)', + '06': 'CM1', + '08': '3B', + '09': 'Zero', + '0a': 'CM3', + '0c': 'Zero W', + '0d': '3B+', + '0e': '3A+', + '0f': 'Internal use only', + '10': 'CM3+', + '11': '4B', + '12': 'Zero 2 W', + '13': '400', + '14': 'CM4' + }; + + const revisionCode = getValue(lines, 'revision', ':', true); + const model = getValue(lines, 'model:', ':', true); + const serial = getValue(lines, 'serial', ':', true); + + let result = {}; + if ({}.hasOwnProperty.call(oldRevisionCodes, revisionCode)) { + // old revision codes + result = { + model, + serial, + revisionCode, + memory: oldRevisionCodes[revisionCode].memory, + manufacturer: oldRevisionCodes[revisionCode].manufacturer, + processor: oldRevisionCodes[revisionCode].processor, + type: oldRevisionCodes[revisionCode].type, + revision: oldRevisionCodes[revisionCode].revision, + }; + + } else { + // new revision code + const revision = ('00000000' + getValue(lines, 'revision', ':', true).toLowerCase()).substr(-8); + // const revisionStyleNew = hex2bin(revision.substr(2, 1)).substr(4, 1) === '1'; + const memSizeCode = parseInt(hex2bin(revision.substr(2, 1)).substr(5, 3), 2) || 0; + const manufacturer = manufacturerList[parseInt(revision.substr(3, 1), 10)]; + const processor = processorList[parseInt(revision.substr(4, 1), 10)]; + const typeCode = revision.substr(5, 2); + + + result = { + model, + serial, + revisionCode, + memory: 256 * Math.pow(2, memSizeCode), + manufacturer, + processor, + type: {}.hasOwnProperty.call(typeList, typeCode) ? typeList[typeCode] : '', + revision: '1.' + revision.substr(7, 1), + }; + } + return result; +} + +function promiseAll(promises) { + const resolvingPromises = promises.map(function (promise) { + return new Promise(function (resolve) { + var payload = new Array(2); + promise.then(function (result) { + payload[0] = result; + }) + .catch(function (error) { + payload[1] = error; + }) + .then(function () { + // The wrapped Promise returns an array: 0 = result, 1 = error ... we resolve all + resolve(payload); + }); + }); + }); + var errors = []; + var results = []; + + // Execute all wrapped Promises + return Promise.all(resolvingPromises) + .then(function (items) { + items.forEach(function (payload) { + if (payload[1]) { + errors.push(payload[1]); + results.push(null); + } else { + errors.push(null); + results.push(payload[0]); + } + }); + + return { + errors: errors, + results: results + }; + }); +} + +function promisify(nodeStyleFunction) { + return function () { + var args = Array.prototype.slice.call(arguments); + return new Promise(function (resolve, reject) { + args.push(function (err, data) { + if (err) { + reject(err); + } else { + resolve(data); + } + }); + nodeStyleFunction.apply(null, args); + }); + }; +} + +function promisifySave(nodeStyleFunction) { + return function () { + var args = Array.prototype.slice.call(arguments); + return new Promise(function (resolve) { + args.push(function (err, data) { + resolve(data); + }); + nodeStyleFunction.apply(null, args); + }); + }; +} + +function linuxVersion() { + let result = ''; + if (_linux) { + try { + result = execSync('uname -v').toString(); + } catch (e) { + result = ''; + } + } + return result; +} + +function plistParser(xmlStr) { + const tags = ['array', 'dict', 'key', 'string', 'integer', 'date', 'real', 'data', 'boolean', 'arrayEmpty']; + const startStr = '<plist version'; + + let pos = xmlStr.indexOf(startStr); + let len = xmlStr.length; + while (xmlStr[pos] !== '>' && pos < len) { + pos++; + } + + let depth = 0; + let inTagStart = false; + let inTagContent = false; + let inTagEnd = false; + let metaData = [{ tagStart: '', tagEnd: '', tagContent: '', key: '', data: null }]; + let c = ''; + let cn = xmlStr[pos]; + + while (pos < len) { + c = cn; + if (pos + 1 < len) { cn = xmlStr[pos + 1]; } + if (c === '<') { + inTagContent = false; + if (cn === '/') { inTagEnd = true; } + else if (metaData[depth].tagStart) { + metaData[depth].tagContent = ''; + if (!metaData[depth].data) { metaData[depth].data = metaData[depth].tagStart === 'array' ? [] : {}; } + depth++; + metaData.push({ tagStart: '', tagEnd: '', tagContent: '', key: null, data: null }); + inTagStart = true; + inTagContent = false; + } + else if (!inTagStart) { inTagStart = true; } + } else if (c === '>') { + if (metaData[depth].tagStart === 'true/') { inTagStart = false; inTagEnd = true; metaData[depth].tagStart = ''; metaData[depth].tagEnd = '/boolean'; metaData[depth].data = true; } + if (metaData[depth].tagStart === 'false/') { inTagStart = false; inTagEnd = true; metaData[depth].tagStart = ''; metaData[depth].tagEnd = '/boolean'; metaData[depth].data = false; } + if (metaData[depth].tagStart === 'array/') { inTagStart = false; inTagEnd = true; metaData[depth].tagStart = ''; metaData[depth].tagEnd = '/arrayEmpty'; metaData[depth].data = []; } + if (inTagContent) { inTagContent = false; } + if (inTagStart) { + inTagStart = false; + inTagContent = true; + if (metaData[depth].tagStart === 'array') { + metaData[depth].data = []; + } + if (metaData[depth].tagStart === 'dict') { + metaData[depth].data = {}; + } + } + if (inTagEnd) { + inTagEnd = false; + if (metaData[depth].tagEnd && tags.indexOf(metaData[depth].tagEnd.substr(1)) >= 0) { + if (metaData[depth].tagEnd === '/dict' || metaData[depth].tagEnd === '/array') { + if (depth > 1 && metaData[depth - 2].tagStart === 'array') { + metaData[depth - 2].data.push(metaData[depth - 1].data); + } + if (depth > 1 && metaData[depth - 2].tagStart === 'dict') { + metaData[depth - 2].data[metaData[depth - 1].key] = metaData[depth - 1].data; + } + depth--; + metaData.pop(); + metaData[depth].tagContent = ''; + metaData[depth].tagStart = ''; + metaData[depth].tagEnd = ''; + } + else { + if (metaData[depth].tagEnd === '/key' && metaData[depth].tagContent) { + metaData[depth].key = metaData[depth].tagContent; + } else { + if (metaData[depth].tagEnd === '/real' && metaData[depth].tagContent) { metaData[depth].data = parseFloat(metaData[depth].tagContent) || 0; } + if (metaData[depth].tagEnd === '/integer' && metaData[depth].tagContent) { metaData[depth].data = parseInt(metaData[depth].tagContent) || 0; } + if (metaData[depth].tagEnd === '/string' && metaData[depth].tagContent) { metaData[depth].data = metaData[depth].tagContent || ''; } + if (metaData[depth].tagEnd === '/boolean') { metaData[depth].data = metaData[depth].tagContent || false; } + if (metaData[depth].tagEnd === '/arrayEmpty') { metaData[depth].data = metaData[depth].tagContent || []; } + if (depth > 0 && metaData[depth - 1].tagStart === 'array') { metaData[depth - 1].data.push(metaData[depth].data); } + if (depth > 0 && metaData[depth - 1].tagStart === 'dict') { metaData[depth - 1].data[metaData[depth].key] = metaData[depth].data; } + } + metaData[depth].tagContent = ''; + metaData[depth].tagStart = ''; + metaData[depth].tagEnd = ''; + } + } + metaData[depth].tagEnd = ''; + inTagStart = false; + inTagContent = false; + } + } else { + if (inTagStart) { metaData[depth].tagStart += c; } + if (inTagEnd) { metaData[depth].tagEnd += c; } + if (inTagContent) { metaData[depth].tagContent += c; } + } + pos++; + } + return metaData[0].data; +} + +function semverCompare(v1, v2) { + let res = 0; + const parts1 = v1.split('.'); + const parts2 = v2.split('.'); + if (parts1[0] < parts2[0]) { res = 1; } + else if (parts1[0] > parts2[0]) { res = -1; } + else if (parts1[0] === parts2[0] && parts1.length >= 2 && parts2.length >= 2) { + if (parts1[1] < parts2[1]) { res = 1; } + else if (parts1[1] > parts2[1]) { res = -1; } + else if (parts1[1] === parts2[1]) { + if (parts1.length >= 3 && parts2.length >= 3) { + if (parts1[2] < parts2[2]) { res = 1; } + else if (parts1[2] > parts2[2]) { res = -1; } + } else if (parts2.length >= 3) { + res = 1; + } + } + } + return res; +} + +function noop() { } + +exports.toInt = toInt; +exports.execOptsWin = execOptsWin; +exports.getCodepage = getCodepage; +exports.execWin = execWin; +exports.isFunction = isFunction; +exports.unique = unique; +exports.sortByKey = sortByKey; +exports.cores = cores; +exports.getValue = getValue; +exports.decodeEscapeSequence = decodeEscapeSequence; +exports.parseDateTime = parseDateTime; +exports.parseHead = parseHead; +exports.findObjectByKey = findObjectByKey; +exports.getWmic = getWmic; +exports.wmic = wmic; +exports.darwinXcodeExists = darwinXcodeExists; +exports.getVboxmanage = getVboxmanage; +exports.powerShell = powerShell; +exports.powerShellStart = powerShellStart; +exports.powerShellRelease = powerShellRelease; +exports.execSafe = execSafe; +exports.nanoSeconds = nanoSeconds; +exports.countUniqueLines = countUniqueLines; +exports.countLines = countLines; +exports.noop = noop; +exports.isRaspberry = isRaspberry; +exports.isRaspbian = isRaspbian; +exports.sanitizeShellString = sanitizeShellString; +exports.isPrototypePolluted = isPrototypePolluted; +exports.decodePiCpuinfo = decodePiCpuinfo; +exports.promiseAll = promiseAll; +exports.promisify = promisify; +exports.promisifySave = promisifySave; +exports.smartMonToolsInstalled = smartMonToolsInstalled; +exports.linuxVersion = linuxVersion; +exports.plistParser = plistParser; +exports.stringReplace = stringReplace; +exports.stringToLower = stringToLower; +exports.stringToString = stringToString; +exports.stringSubstr = stringSubstr; +exports.stringTrim = stringTrim; +exports.stringStartWith = stringStartWith; +exports.mathMin = mathMin; +exports.WINDIR = WINDIR; +exports.getFilesInPath = getFilesInPath; +exports.semverCompare = semverCompare; diff --git a/node_modules/systeminformation/lib/virtualbox.js b/node_modules/systeminformation/lib/virtualbox.js new file mode 100644 index 0000000..61268b6 --- /dev/null +++ b/node_modules/systeminformation/lib/virtualbox.js @@ -0,0 +1,107 @@ +'use strict'; +// @ts-check +// ================================================================================== +// virtualbox.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 14. Docker +// ---------------------------------------------------------------------------------- + +const os = require('os'); +const exec = require('child_process').exec; +const util = require('./util'); + +function vboxInfo(callback) { + + // fallback - if only callback is given + let result = []; + return new Promise((resolve) => { + process.nextTick(() => { + try { + exec(util.getVboxmanage() + ' list vms --long', function (error, stdout) { + let parts = (os.EOL + stdout.toString()).split(os.EOL + 'Name:'); + parts.shift(); + parts.forEach(part => { + const lines = ('Name:' + part).split(os.EOL); + const state = util.getValue(lines, 'State'); + const running = state.startsWith('running'); + const runningSinceString = running ? state.replace('running (since ', '').replace(')', '').trim() : ''; + let runningSince = 0; + try { + if (running) { + const sinceDateObj = new Date(runningSinceString); + const offset = sinceDateObj.getTimezoneOffset(); + runningSince = Math.round((Date.now() - Date.parse(sinceDateObj)) / 1000) + offset * 60; + } + } catch (e) { + util.noop(); + } + const stoppedSinceString = !running ? state.replace('powered off (since', '').replace(')', '').trim() : ''; + let stoppedSince = 0; + try { + if (!running) { + const sinceDateObj = new Date(stoppedSinceString); + const offset = sinceDateObj.getTimezoneOffset(); + stoppedSince = Math.round((Date.now() - Date.parse(sinceDateObj)) / 1000) + offset * 60; + } + } catch (e) { + util.noop(); + } + result.push({ + id: util.getValue(lines, 'UUID'), + name: util.getValue(lines, 'Name'), + running, + started: runningSinceString, + runningSince, + stopped: stoppedSinceString, + stoppedSince, + guestOS: util.getValue(lines, 'Guest OS'), + hardwareUUID: util.getValue(lines, 'Hardware UUID'), + memory: parseInt(util.getValue(lines, 'Memory size', ' '), 10), + vram: parseInt(util.getValue(lines, 'VRAM size'), 10), + cpus: parseInt(util.getValue(lines, 'Number of CPUs'), 10), + cpuExepCap: util.getValue(lines, 'CPU exec cap'), + cpuProfile: util.getValue(lines, 'CPUProfile'), + chipset: util.getValue(lines, 'Chipset'), + firmware: util.getValue(lines, 'Firmware'), + pageFusion: util.getValue(lines, 'Page Fusion') === 'enabled', + configFile: util.getValue(lines, 'Config file'), + snapshotFolder: util.getValue(lines, 'Snapshot folder'), + logFolder: util.getValue(lines, 'Log folder'), + hpet: util.getValue(lines, 'HPET') === 'enabled', + pae: util.getValue(lines, 'PAE') === 'enabled', + longMode: util.getValue(lines, 'Long Mode') === 'enabled', + tripleFaultReset: util.getValue(lines, 'Triple Fault Reset') === 'enabled', + apic: util.getValue(lines, 'APIC') === 'enabled', + x2Apic: util.getValue(lines, 'X2APIC') === 'enabled', + acpi: util.getValue(lines, 'ACPI') === 'enabled', + ioApic: util.getValue(lines, 'IOAPIC') === 'enabled', + biosApicMode: util.getValue(lines, 'BIOS APIC mode'), + bootMenuMode: util.getValue(lines, 'Boot menu mode'), + bootDevice1: util.getValue(lines, 'Boot Device 1'), + bootDevice2: util.getValue(lines, 'Boot Device 2'), + bootDevice3: util.getValue(lines, 'Boot Device 3'), + bootDevice4: util.getValue(lines, 'Boot Device 4'), + timeOffset: util.getValue(lines, 'Time offset'), + rtc: util.getValue(lines, 'RTC'), + }); + }); + + if (callback) { callback(result); } + resolve(result); + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + }); + }); +} + +exports.vboxInfo = vboxInfo; diff --git a/node_modules/systeminformation/lib/wifi.js b/node_modules/systeminformation/lib/wifi.js new file mode 100644 index 0000000..cd8fa71 --- /dev/null +++ b/node_modules/systeminformation/lib/wifi.js @@ -0,0 +1,747 @@ +'use strict'; +// @ts-check +// ================================================================================== +// wifi.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 9. wifi +// ---------------------------------------------------------------------------------- + +const os = require('os'); +const exec = require('child_process').exec; +const execSync = require('child_process').execSync; +const util = require('./util'); + +let _platform = process.platform; + +const _linux = (_platform === 'linux' || _platform === 'android'); +const _darwin = (_platform === 'darwin'); +const _windows = (_platform === 'win32'); + +function wifiDBFromQuality(quality) { + return (parseFloat(quality) / 2 - 100); +} + +function wifiQualityFromDB(db) { + const result = 2 * (parseFloat(db) + 100); + return result <= 100 ? result : 100; +} + +const _wifi_frequencies = { + 1: 2412, + 2: 2417, + 3: 2422, + 4: 2427, + 5: 2432, + 6: 2437, + 7: 2442, + 8: 2447, + 9: 2452, + 10: 2457, + 11: 2462, + 12: 2467, + 13: 2472, + 14: 2484, + 32: 5160, + 34: 5170, + 36: 5180, + 38: 5190, + 40: 5200, + 42: 5210, + 44: 5220, + 46: 5230, + 48: 5240, + 50: 5250, + 52: 5260, + 54: 5270, + 56: 5280, + 58: 5290, + 60: 5300, + 62: 5310, + 64: 5320, + 68: 5340, + 96: 5480, + 100: 5500, + 102: 5510, + 104: 5520, + 106: 5530, + 108: 5540, + 110: 5550, + 112: 5560, + 114: 5570, + 116: 5580, + 118: 5590, + 120: 5600, + 122: 5610, + 124: 5620, + 126: 5630, + 128: 5640, + 132: 5660, + 134: 5670, + 136: 5680, + 138: 5690, + 140: 5700, + 142: 5710, + 144: 5720, + 149: 5745, + 151: 5755, + 153: 5765, + 155: 5775, + 157: 5785, + 159: 5795, + 161: 5805, + 165: 5825, + 169: 5845, + 173: 5865, + 183: 4915, + 184: 4920, + 185: 4925, + 187: 4935, + 188: 4940, + 189: 4945, + 192: 4960, + 196: 4980 +}; + +function wifiFrequencyFromChannel(channel) { + return {}.hasOwnProperty.call(_wifi_frequencies, channel) ? _wifi_frequencies[channel] : null; +} + +function wifiChannelFromFrequencs(frequency) { + let channel = 0; + for (let key in _wifi_frequencies) { + if ({}.hasOwnProperty.call(_wifi_frequencies, key)) { + if (_wifi_frequencies[key] === frequency) { channel = util.toInt(key); } + } + } + return channel; +} + +function ifaceListLinux() { + const result = []; + const cmd = 'iw dev'; + try { + const all = execSync(cmd).toString().split('\n').map(line => line.trim()).join('\n'); + const parts = all.split('\nInterface '); + parts.shift(); + parts.forEach(ifaceDetails => { + const lines = ifaceDetails.split('\n'); + const iface = lines[0]; + const id = util.toInt(util.getValue(lines, 'ifindex', ' ')); + const mac = util.getValue(lines, 'addr', ' '); + const channel = util.toInt(util.getValue(lines, 'channel', ' ')); + result.push({ + id, + iface, + mac, + channel + }); + }); + return result; + } catch (e) { + return []; + } +} + +function nmiDeviceLinux(iface) { + const cmd = `nmcli -t -f general,wifi-properties,capabilities,ip4,ip6 device show ${iface} 2>/dev/null`; + try { + const lines = execSync(cmd).toString().split('\n'); + const ssid = util.getValue(lines, 'GENERAL.CONNECTION'); + return { + iface, + type: util.getValue(lines, 'GENERAL.TYPE'), + vendor: util.getValue(lines, 'GENERAL.VENDOR'), + product: util.getValue(lines, 'GENERAL.PRODUCT'), + mac: util.getValue(lines, 'GENERAL.HWADDR').toLowerCase(), + ssid: ssid !== '--' ? ssid : null + }; + } catch (e) { + return {}; + } +} + +function nmiConnectionLinux(ssid) { + const cmd = `nmcli -t --show-secrets connection show ${ssid} 2>/dev/null`; + try { + const lines = execSync(cmd).toString().split('\n'); + const bssid = util.getValue(lines, '802-11-wireless.seen-bssids').toLowerCase(); + return { + ssid: ssid !== '--' ? ssid : null, + uuid: util.getValue(lines, 'connection.uuid'), + type: util.getValue(lines, 'connection.type'), + autoconnect: util.getValue(lines, 'connection.autoconnect') === 'yes', + security: util.getValue(lines, '802-11-wireless-security.key-mgmt'), + bssid: bssid !== '--' ? bssid : null + }; + } catch (e) { + return {}; + } +} + +function wpaConnectionLinux(iface) { + const cmd = `wpa_cli -i ${iface} status 2>&1`; + try { + const lines = execSync(cmd).toString().split('\n'); + const freq = util.toInt(util.getValue(lines, 'freq', '=')); + return { + ssid: util.getValue(lines, 'ssid', '='), + uuid: util.getValue(lines, 'uuid', '='), + security: util.getValue(lines, 'key_mgmt', '='), + freq, + channel: wifiChannelFromFrequencs(freq), + bssid: util.getValue(lines, 'bssid', '=').toLowerCase() + }; + } catch (e) { + return {}; + } +} + +function getWifiNetworkListNmi() { + const result = []; + const cmd = 'nmcli -t -m multiline --fields active,ssid,bssid,mode,chan,freq,signal,security,wpa-flags,rsn-flags device wifi list 2>/dev/null'; + try { + const stdout = execSync(cmd, { maxBuffer: 1024 * 20000 }); + const parts = stdout.toString().split('ACTIVE:'); + parts.shift(); + parts.forEach(part => { + part = 'ACTIVE:' + part; + const lines = part.split(os.EOL); + const channel = util.getValue(lines, 'CHAN'); + const frequency = util.getValue(lines, 'FREQ').toLowerCase().replace('mhz', '').trim(); + const security = util.getValue(lines, 'SECURITY').replace('(', '').replace(')', ''); + const wpaFlags = util.getValue(lines, 'WPA-FLAGS').replace('(', '').replace(')', ''); + const rsnFlags = util.getValue(lines, 'RSN-FLAGS').replace('(', '').replace(')', ''); + result.push({ + ssid: util.getValue(lines, 'SSID'), + bssid: util.getValue(lines, 'BSSID').toLowerCase(), + mode: util.getValue(lines, 'MODE'), + channel: channel ? parseInt(channel, 10) : null, + frequency: frequency ? parseInt(frequency, 10) : null, + signalLevel: wifiDBFromQuality(util.getValue(lines, 'SIGNAL')), + quality: parseFloat(util.getValue(lines, 'SIGNAL')), + security: security && security !== 'none' ? security.split(' ') : [], + wpaFlags: wpaFlags && wpaFlags !== 'none' ? wpaFlags.split(' ') : [], + rsnFlags: rsnFlags && rsnFlags !== 'none' ? rsnFlags.split(' ') : [] + }); + }); + return result; + } catch (e) { + return []; + } +} + +function getWifiNetworkListIw(iface) { + const result = []; + try { + let iwlistParts = execSync(`export LC_ALL=C; iwlist ${iface} scan 2>&1; unset LC_ALL`).toString().split(' Cell '); + if (iwlistParts[0].indexOf('resource busy') >= 0) { return -1; } + if (iwlistParts.length > 1) { + iwlistParts.shift(); + for (let i = 0; i < iwlistParts.length; i++) { + const lines = iwlistParts[i].split('\n'); + const channel = util.getValue(lines, 'channel', ':', true); + const address = (lines && lines.length && lines[0].indexOf('Address:') >= 0 ? lines[0].split('Address:')[1].trim().toLowerCase() : ''); + const mode = util.getValue(lines, 'mode', ':', true); + const frequency = util.getValue(lines, 'frequency', ':', true); + const qualityString = util.getValue(lines, 'Quality', '=', true); + const dbParts = qualityString.toLowerCase().split('signal level='); + const db = dbParts.length > 1 ? util.toInt(dbParts[1]) : 0; + const quality = db ? wifiQualityFromDB(db) : 0; + const ssid = util.getValue(lines, 'essid', ':', true); + + // security and wpa-flags + const isWpa = iwlistParts[i].indexOf(' WPA ') >= 0; + const isWpa2 = iwlistParts[i].indexOf('WPA2 ') >= 0; + const security = []; + if (isWpa) { security.push('WPA'); } + if (isWpa2) { security.push('WPA2'); } + const wpaFlags = []; + let wpaFlag = ''; + lines.forEach(function (line) { + const l = line.trim().toLowerCase(); + if (l.indexOf('group cipher') >= 0) { + if (wpaFlag) { + wpaFlags.push(wpaFlag); + } + const parts = l.split(':'); + if (parts.length > 1) { + wpaFlag = parts[1].trim().toUpperCase(); + } + } + if (l.indexOf('pairwise cipher') >= 0) { + const parts = l.split(':'); + if (parts.length > 1) { + if (parts[1].indexOf('tkip')) { wpaFlag = (wpaFlag ? 'TKIP/' + wpaFlag : 'TKIP'); } + else if (parts[1].indexOf('ccmp')) { wpaFlag = (wpaFlag ? 'CCMP/' + wpaFlag : 'CCMP'); } + else if (parts[1].indexOf('proprietary')) { wpaFlag = (wpaFlag ? 'PROP/' + wpaFlag : 'PROP'); } + } + } + if (l.indexOf('authentication suites') >= 0) { + const parts = l.split(':'); + if (parts.length > 1) { + if (parts[1].indexOf('802.1x')) { wpaFlag = (wpaFlag ? '802.1x/' + wpaFlag : '802.1x'); } + else if (parts[1].indexOf('psk')) { wpaFlag = (wpaFlag ? 'PSK/' + wpaFlag : 'PSK'); } + } + } + }); + if (wpaFlag) { + wpaFlags.push(wpaFlag); + } + + result.push({ + ssid, + bssid: address, + mode, + channel: channel ? util.toInt(channel) : null, + frequency: frequency ? util.toInt(frequency.replace('.', '')) : null, + signalLevel: db, + quality, + security, + wpaFlags, + rsnFlags: [] + }); + } + } + return result; + } catch (e) { + return -1; + } +} + +/* + ssid: line.substring(parsedhead[0].from, parsedhead[0].to).trim(), + bssid: line.substring(parsedhead[1].from, parsedhead[1].to).trim().toLowerCase(), + mode: '', + channel, + frequency: wifiFrequencyFromChannel(channel), + signalLevel: signalLevel ? parseInt(signalLevel, 10) : null, + quality: wifiQualityFromDB(signalLevel), + security, + wpaFlags, + rsnFlags: [] + + const securityAll = line.substring(parsedhead[6].from, 1000).trim().split(' '); + let security = []; + let wpaFlags = []; + securityAll.forEach(securitySingle => { + if (securitySingle.indexOf('(') > 0) { + const parts = securitySingle.split('('); + security.push(parts[0]); + wpaFlags = wpaFlags.concat(parts[1].replace(')', '').split(',')); + } + }); + +*/ +function parseWifiDarwin(wifiObj) { + const result = []; + if (wifiObj) { + wifiObj.forEach(function (wifiItem) { + const signalLevel = wifiItem.RSSI; + let security = []; + let wpaFlags = []; + if (wifiItem.WPA_IE) { + security.push('WPA'); + if (wifiItem.WPA_IE.IE_KEY_WPA_UCIPHERS) { + wifiItem.WPA_IE.IE_KEY_WPA_UCIPHERS.forEach(function (ciphers) { + if (ciphers === 0 && wpaFlags.indexOf('unknown/TKIP') === -1) { wpaFlags.push('unknown/TKIP'); } + if (ciphers === 2 && wpaFlags.indexOf('PSK/TKIP') === -1) { wpaFlags.push('PSK/TKIP'); } + if (ciphers === 4 && wpaFlags.indexOf('PSK/AES') === -1) { wpaFlags.push('PSK/AES'); } + }); + } + } + if (wifiItem.RSN_IE) { + security.push('WPA2'); + if (wifiItem.RSN_IE.IE_KEY_RSN_UCIPHERS) { + wifiItem.RSN_IE.IE_KEY_RSN_UCIPHERS.forEach(function (ciphers) { + if (ciphers === 0 && wpaFlags.indexOf('unknown/TKIP') === -1) { wpaFlags.push('unknown/TKIP'); } + if (ciphers === 2 && wpaFlags.indexOf('TKIP/TKIP') === -1) { wpaFlags.push('TKIP/TKIP'); } + if (ciphers === 4 && wpaFlags.indexOf('PSK/AES') === -1) { wpaFlags.push('PSK/AES'); } + }); + } + } + result.push({ + ssid: wifiItem.SSID_STR, + bssid: wifiItem.BSSID, + mode: '', + channel: wifiItem.CHANNEL, + frequency: wifiFrequencyFromChannel(wifiItem.CHANNEL), + signalLevel: signalLevel ? parseInt(signalLevel, 10) : null, + quality: wifiQualityFromDB(signalLevel), + security, + wpaFlags, + rsnFlags: [] + }); + }); + } + return result; +} +function wifiNetworks(callback) { + return new Promise((resolve) => { + process.nextTick(() => { + let result = []; + if (_linux) { + result = getWifiNetworkListNmi(); + if (result.length === 0) { + try { + const iwconfigParts = execSync('export LC_ALL=C; iwconfig 2>/dev/null; unset LC_ALL').toString().split('\n\n'); + let iface = ''; + for (let i = 0; i < iwconfigParts.length; i++) { + if (iwconfigParts[i].indexOf('no wireless') === -1 && iwconfigParts[i].trim() !== '') { + iface = iwconfigParts[i].split(' ')[0]; + } + } + if (iface) { + const res = getWifiNetworkListIw(iface); + if (res === -1) { + // try again after 4 secs + setTimeout(function (iface) { + const res = getWifiNetworkListIw(iface); + if (res != -1) { result = res; } + if (callback) { + callback(result); + } + resolve(result); + }, 4000); + } else { + result = res; + if (callback) { + callback(result); + } + resolve(result); + } + } else { + if (callback) { + callback(result); + } + resolve(result); + } + } catch (e) { + if (callback) { + callback(result); + } + resolve(result); + } + } else { + if (callback) { + callback(result); + } + resolve(result); + } + } else if (_darwin) { + let cmd = '/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -s -x'; + exec(cmd, { maxBuffer: 1024 * 40000 }, function (error, stdout) { + const output = stdout.toString(); + result = parseWifiDarwin(util.plistParser(output)); + if (callback) { + callback(result); + } + resolve(result); + }); + } else if (_windows) { + let cmd = 'netsh wlan show networks mode=Bssid'; + util.powerShell(cmd).then((stdout) => { + const ssidParts = stdout.toString('utf8').split(os.EOL + os.EOL + 'SSID '); + ssidParts.shift(); + + ssidParts.forEach(ssidPart => { + const ssidLines = ssidPart.split(os.EOL); + if (ssidLines && ssidLines.length >= 8 && ssidLines[0].indexOf(':') >= 0) { + const bssidsParts = ssidPart.split(' BSSID'); + bssidsParts.shift(); + + bssidsParts.forEach((bssidPart) => { + const bssidLines = bssidPart.split(os.EOL); + const bssidLine = bssidLines[0].split(':'); + bssidLine.shift(); + const bssid = bssidLine.join(':').trim().toLowerCase(); + const channel = bssidLines[3].split(':').pop().trim(); + const quality = bssidLines[1].split(':').pop().trim(); + + result.push({ + ssid: ssidLines[0].split(':').pop().trim(), + bssid, + mode: '', + channel: channel ? parseInt(channel, 10) : null, + frequency: wifiFrequencyFromChannel(channel), + signalLevel: wifiDBFromQuality(quality), + quality: quality ? parseInt(quality, 10) : null, + security: [ssidLines[2].split(':').pop().trim()], + wpaFlags: [ssidLines[3].split(':').pop().trim()], + rsnFlags: [] + }); + }); + } + }); + + if (callback) { + callback(result); + } + resolve(result); + }); + } else { + if (callback) { + callback(result); + } + resolve(result); + } + }); + }); +} + +exports.wifiNetworks = wifiNetworks; + +function getVendor(model) { + model = model.toLowerCase(); + let result = ''; + if (model.indexOf('intel') >= 0) { result = 'Intel'; } + else if (model.indexOf('realtek') >= 0) { result = 'Realtek'; } + else if (model.indexOf('qualcom') >= 0) { result = 'Qualcom'; } + else if (model.indexOf('broadcom') >= 0) { result = 'Broadcom'; } + else if (model.indexOf('cavium') >= 0) { result = 'Cavium'; } + else if (model.indexOf('cisco') >= 0) { result = 'Cisco'; } + else if (model.indexOf('marvel') >= 0) { result = 'Marvel'; } + else if (model.indexOf('zyxel') >= 0) { result = 'Zyxel'; } + else if (model.indexOf('melanox') >= 0) { result = 'Melanox'; } + else if (model.indexOf('d-link') >= 0) { result = 'D-Link'; } + else if (model.indexOf('tp-link') >= 0) { result = 'TP-Link'; } + else if (model.indexOf('asus') >= 0) { result = 'Asus'; } + else if (model.indexOf('linksys') >= 0) { result = 'Linksys'; } + return result; +} + +function wifiConnections(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + const result = []; + + if (_linux) { + const ifaces = ifaceListLinux(); + const networkList = getWifiNetworkListNmi(); + ifaces.forEach(ifaceDetail => { + const nmiDetails = nmiDeviceLinux(ifaceDetail.iface); + const wpaDetails = wpaConnectionLinux(ifaceDetail.iface); + const ssid = nmiDetails.ssid || wpaDetails.ssid; + const network = networkList.filter(nw => nw.ssid === ssid); + const nmiConnection = nmiConnectionLinux(ssid); + const channel = network && network.length && network[0].channel ? network[0].channel : (wpaDetails.channel ? wpaDetails.channel : null); + const bssid = network && network.length && network[0].bssid ? network[0].bssid : (wpaDetails.bssid ? wpaDetails.bssid : null); + if (ssid && bssid) { + result.push({ + id: ifaceDetail.id, + iface: ifaceDetail.iface, + model: nmiDetails.product, + ssid, + bssid: network && network.length && network[0].bssid ? network[0].bssid : (wpaDetails.bssid ? wpaDetails.bssid : null), + channel, + frequency: channel ? wifiFrequencyFromChannel(channel) : null, + type: nmiConnection.type ? nmiConnection.type : '802.11', + security: nmiConnection.security ? nmiConnection.security : (wpaDetails.security ? wpaDetails.security : null), + signalLevel: network && network.length && network[0].signalLevel ? network[0].signalLevel : null, + txRate: null + }); + } + }); + if (callback) { + callback(result); + } + resolve(result); + } else if (_darwin) { + let cmd = 'system_profiler SPNetworkDataType'; + exec(cmd, function (error, stdout) { + const parts1 = stdout.toString().split('\n\n Wi-Fi:\n\n'); + if (parts1.length > 1) { + const lines = parts1[1].split('\n\n')[0].split('\n'); + const iface = util.getValue(lines, 'BSD Device Name', ':', true); + const model = util.getValue(lines, 'hardware', ':', true); + cmd = '/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I'; + exec(cmd, function (error, stdout) { + const lines2 = stdout.toString().split('\n'); + if (lines.length > 10) { + const ssid = util.getValue(lines2, 'ssid', ':', true); + const bssid = util.getValue(lines2, 'bssid', ':', true); + const security = util.getValue(lines2, 'link auth', ':', true); + const txRate = util.getValue(lines2, 'lastTxRate', ':', true); + const channel = util.getValue(lines2, 'channel', ':', true).split(',')[0]; + const type = '802.11'; + const rssi = util.toInt(util.getValue(lines2, 'agrCtlRSSI', ':', true)); + const noise = util.toInt(util.getValue(lines2, 'agrCtlNoise', ':', true)); + const signalLevel = rssi - noise; + // const signal = wifiQualityFromDB(signalLevel); + if (ssid || bssid) { + result.push({ + id: 'Wi-Fi', + iface, + model, + ssid, + bssid, + channel: util.toInt(channel), + frequency: channel ? wifiFrequencyFromChannel(channel) : null, + type, + security, + signalLevel, + txRate + }); + + } + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + }); + } else if (_windows) { + let cmd = 'netsh wlan show interfaces'; + util.powerShell(cmd).then(function (stdout) { + const allLines = stdout.toString().split('\r\n'); + for (let i = 0; i < allLines.length; i++) { + allLines[i] = allLines[i].trim(); + } + const parts = allLines.join('\r\n').split(':\r\n\r\n'); + parts.shift(); + parts.forEach(part => { + const lines = part.split('\r\n'); + if (lines.length >= 5) { + const iface = lines[0].indexOf(':') >= 0 ? lines[0].split(':')[1].trim() : ''; + const model = lines[1].indexOf(':') >= 0 ? lines[1].split(':')[1].trim() : ''; + const id = lines[2].indexOf(':') >= 0 ? lines[2].split(':')[1].trim() : ''; + const ssid = util.getValue(lines, 'SSID', ':', true); + const bssid = util.getValue(lines, 'BSSID', ':', true); + const signalLevel = util.getValue(lines, 'Signal', ':', true); + const type = util.getValue(lines, 'Radio type', ':', true) || util.getValue(lines, 'Type de radio', ':', true) || util.getValue(lines, 'Funktyp', ':', true) || null; + const security = util.getValue(lines, 'authentication', ':', true) || util.getValue(lines, 'Authentification', ':', true) || util.getValue(lines, 'Authentifizierung', ':', true) || null; + const channel = util.getValue(lines, 'Channel', ':', true) || util.getValue(lines, 'Canal', ':', true) || util.getValue(lines, 'Kanal', ':', true) || null; + const txRate = util.getValue(lines, 'Transmit rate (mbps)', ':', true) || util.getValue(lines, 'Transmission (mbit/s)', ':', true) || util.getValue(lines, 'Empfangsrate (MBit/s)', ':', true) || null; + if (model && id && ssid && bssid) { + result.push({ + id, + iface, + model, + ssid, + bssid, + channel: util.toInt(channel), + frequency: channel ? wifiFrequencyFromChannel(channel) : null, + type, + security, + signalLevel, + txRate: util.toInt(txRate) || null + }); + } + } + }); + if (callback) { + callback(result); + } + resolve(result); + }); + } else { + if (callback) { + callback(result); + } + resolve(result); + } + }); + }); +} + +exports.wifiConnections = wifiConnections; + +function wifiInterfaces(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + const result = []; + + if (_linux) { + const ifaces = ifaceListLinux(); + ifaces.forEach(ifaceDetail => { + const nmiDetails = nmiDeviceLinux(ifaceDetail.iface); + result.push({ + id: ifaceDetail.id, + iface: ifaceDetail.iface, + model: nmiDetails.product ? nmiDetails.product : null, + vendor: nmiDetails.vendor ? nmiDetails.vendor : null, + mac: ifaceDetail.mac, + }); + }); + if (callback) { + callback(result); + } + resolve(result); + } else if (_darwin) { + let cmd = 'system_profiler SPNetworkDataType'; + exec(cmd, function (error, stdout) { + const parts1 = stdout.toString().split('\n\n Wi-Fi:\n\n'); + if (parts1.length > 1) { + const lines = parts1[1].split('\n\n')[0].split('\n'); + const iface = util.getValue(lines, 'BSD Device Name', ':', true); + const mac = util.getValue(lines, 'MAC Address', ':', true); + const model = util.getValue(lines, 'hardware', ':', true); + result.push({ + id: 'Wi-Fi', + iface, + model, + vendor: '', + mac + }); + } + if (callback) { + callback(result); + } + resolve(result); + }); + } else if (_windows) { + let cmd = 'netsh wlan show interfaces'; + util.powerShell(cmd).then(function (stdout) { + const allLines = stdout.toString().split('\r\n'); + for (let i = 0; i < allLines.length; i++) { + allLines[i] = allLines[i].trim(); + } + const parts = allLines.join('\r\n').split(':\r\n\r\n'); + parts.shift(); + parts.forEach(part => { + const lines = part.split('\r\n'); + if (lines.length >= 5) { + const iface = lines[0].indexOf(':') >= 0 ? lines[0].split(':')[1].trim() : ''; + const model = lines[1].indexOf(':') >= 0 ? lines[1].split(':')[1].trim() : ''; + const id = lines[2].indexOf(':') >= 0 ? lines[2].split(':')[1].trim() : ''; + const macParts = lines[3].indexOf(':') >= 0 ? lines[3].split(':') : []; + macParts.shift(); + const mac = macParts.join(':').trim(); + const vendor = getVendor(model); + if (iface && model && id && mac) { + result.push({ + id, + iface, + model, + vendor, + mac, + }); + } + } + }); + if (callback) { + callback(result); + } + resolve(result); + }); + } else { + if (callback) { + callback(result); + } + resolve(result); + } + }); + }); +} + +exports.wifiInterfaces = wifiInterfaces; diff --git a/node_modules/systeminformation/package.json b/node_modules/systeminformation/package.json new file mode 100644 index 0000000..d1f3e08 --- /dev/null +++ b/node_modules/systeminformation/package.json @@ -0,0 +1,99 @@ +{ + "name": "systeminformation", + "version": "5.11.22", + "description": "Advanced, lightweight system and OS information library", + "license": "MIT", + "author": "Sebastian Hildebrandt <hildebrandt@plus-innovations.com> (https://plus-innovations.com)", + "homepage": "https://systeminformation.io", + "main": "./lib/index.js", + "bin": { + "systeminformation": "lib/cli.js" + }, + "types": "./lib/index.d.ts", + "scripts": { + "test": "node ./test/test.js" + }, + "files": [ + "lib/" + ], + "keywords": [ + "system information", + "sysinfo", + "monitor", + "monitoring", + "os", + "linux", + "osx", + "windows", + "freebsd", + "openbsd", + "netbsd", + "cpu", + "cpuload", + "physical cores", + "logical cores", + "processor", + "cores", + "threads", + "socket type", + "memory", + "file system", + "fsstats", + "diskio", + "block devices", + "netstats", + "network", + "network interfaces", + "network connections", + "network stats", + "iface", + "printer", + "processes", + "users", + "internet", + "battery", + "docker", + "docker stats", + "docker processes", + "graphics", + "graphic card", + "graphic controller", + "gpu", + "display", + "smart", + "disk layout", + "usb", + "audio", + "bluetooth", + "wifi", + "wifinetworks", + "virtual box", + "virtualbox", + "vm", + "backend", + "hardware", + "BIOS", + "chassis" + ], + "repository": { + "type": "git", + "url": "https://github.com/sebhildebrandt/systeminformation.git" + }, + "funding": { + "type": "Buy me a coffee", + "url": "https://www.buymeacoffee.com/systeminfo" + }, + "os": [ + "darwin", + "linux", + "win32", + "freebsd", + "openbsd", + "netbsd", + "sunos", + "android" + ], + "engines": { + "node": ">=8.0.0" + } +} diff --git a/node_modules/terminal-escape-char/.npmignore b/node_modules/terminal-escape-char/.npmignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/node_modules/terminal-escape-char/.npmignore @@ -0,0 +1 @@ +node_modules
\ No newline at end of file diff --git a/node_modules/terminal-escape-char/.travis.yml b/node_modules/terminal-escape-char/.travis.yml new file mode 100644 index 0000000..ab2fee2 --- /dev/null +++ b/node_modules/terminal-escape-char/.travis.yml @@ -0,0 +1,6 @@ +language: node_js +node_js: + - '5' + - '4' + - '0.12' + - '0.10' diff --git a/node_modules/terminal-escape-char/index.js b/node_modules/terminal-escape-char/index.js new file mode 100644 index 0000000..2152d08 --- /dev/null +++ b/node_modules/terminal-escape-char/index.js @@ -0,0 +1,5 @@ +"use strict"; + +var escapeCharacter = "\u001b" + +module.exports = escapeCharacter; diff --git a/node_modules/terminal-escape-char/package.json b/node_modules/terminal-escape-char/package.json new file mode 100644 index 0000000..0406843 --- /dev/null +++ b/node_modules/terminal-escape-char/package.json @@ -0,0 +1,25 @@ +{ + "name": "terminal-escape-char", + "version": "0.0.4", + "description": "Get [Escape Character](https://en.wikipedia.org/wiki/Escape_character)", + "main": "index.js", + "scripts": { + "test": "mocha test.js" + }, + "repository": "joegesualdo/terminal-escape-char", + "keywords": [ + "escape", + "char", + "terminal", + "ansi" + ], + "author": { + "name": "Joe Gesualdo" + }, + "license": "MIT", + "devDependencies": { + "mocha": "*", + "chai": "*" + }, + "dependencies": {} +} diff --git a/node_modules/terminal-escape-char/readme.md b/node_modules/terminal-escape-char/readme.md new file mode 100644 index 0000000..8cad01b --- /dev/null +++ b/node_modules/terminal-escape-char/readme.md @@ -0,0 +1,22 @@ +## Terminal Escape Character [![Build Status](https://travis-ci.org/joegesualdo/terminal-escape-char.svg?branch=master)](https://travis-ci.org/joegesualdo/terminal-escape-char) +> Get [Escape Character](https://en.wikipedia.org/wiki/Escape_character) + +## Install +``` +$ npm install --save terminal-escape-char +``` + +## Usage +```javascript +var escapeChar = require("terminal-escape-char") + +console.log(escapeChar) +``` + +## Test +``` +$ npm test +``` + +## License +MIT © [Joe Gesualdo]() diff --git a/node_modules/terminal-escape-char/test.js b/node_modules/terminal-escape-char/test.js new file mode 100644 index 0000000..f47050e --- /dev/null +++ b/node_modules/terminal-escape-char/test.js @@ -0,0 +1,8 @@ +var expect = require("chai").expect +var escapeChar = require("./index") + +describe("escapeChar", function(){ + it("is correct", function(){ + expect(escapeChar).to.equal("\u001b") + }) +}) diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..ca6c6f8 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,104 @@ +{ + "name": "strawberry-boot", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "chalk": "^5.0.1", + "hide-terminal-cursor": "^0.0.1", + "systeminformation": "^5.11.22" + } + }, + "node_modules/chalk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", + "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/control-sequence-introducer": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/control-sequence-introducer/-/control-sequence-introducer-0.0.2.tgz", + "integrity": "sha512-UZ8wDB3b0dEIkmUaaiDuMgNiChcBSVel5iKv+77DZ0UGrf0UnJ4Kxnp9KyrcOezDiUU4ryDyq+BODOAMcRv5+g==", + "dependencies": { + "terminal-escape-char": "*" + } + }, + "node_modules/hide-terminal-cursor": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/hide-terminal-cursor/-/hide-terminal-cursor-0.0.1.tgz", + "integrity": "sha512-HB4zGVQrBpzmexWv8+YrmIzc+Lsc/jXkMJnVY25zVoJ61Ig+pTBI6snpg7fTvjGYeQPeByp8vVCX8YHuNGX7XQ==", + "dependencies": { + "control-sequence-introducer": "0.0.2" + } + }, + "node_modules/systeminformation": { + "version": "5.11.22", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.11.22.tgz", + "integrity": "sha512-sBZJ/WBCf2vDLeMZaEyVuo+aXylOSmNHHB2cX0jHULFxSBLXHX+QUHYrCvmz+YiflKY3bsahVWX7vwuz1p1QZA==", + "os": [ + "darwin", + "linux", + "win32", + "freebsd", + "openbsd", + "netbsd", + "sunos", + "android" + ], + "bin": { + "systeminformation": "lib/cli.js" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "Buy me a coffee", + "url": "https://www.buymeacoffee.com/systeminfo" + } + }, + "node_modules/terminal-escape-char": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/terminal-escape-char/-/terminal-escape-char-0.0.4.tgz", + "integrity": "sha512-Gy0H5xwAGLKgQpid7kKRgRgrwqiVQe0b8uduRZr6mDBVH21BwUhDJEVvhBEONDt6Or+gcur0QeQDOr6rAmwnpQ==" + } + }, + "dependencies": { + "chalk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", + "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==" + }, + "control-sequence-introducer": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/control-sequence-introducer/-/control-sequence-introducer-0.0.2.tgz", + "integrity": "sha512-UZ8wDB3b0dEIkmUaaiDuMgNiChcBSVel5iKv+77DZ0UGrf0UnJ4Kxnp9KyrcOezDiUU4ryDyq+BODOAMcRv5+g==", + "requires": { + "terminal-escape-char": "*" + } + }, + "hide-terminal-cursor": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/hide-terminal-cursor/-/hide-terminal-cursor-0.0.1.tgz", + "integrity": "sha512-HB4zGVQrBpzmexWv8+YrmIzc+Lsc/jXkMJnVY25zVoJ61Ig+pTBI6snpg7fTvjGYeQPeByp8vVCX8YHuNGX7XQ==", + "requires": { + "control-sequence-introducer": "0.0.2" + } + }, + "systeminformation": { + "version": "5.11.22", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.11.22.tgz", + "integrity": "sha512-sBZJ/WBCf2vDLeMZaEyVuo+aXylOSmNHHB2cX0jHULFxSBLXHX+QUHYrCvmz+YiflKY3bsahVWX7vwuz1p1QZA==" + }, + "terminal-escape-char": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/terminal-escape-char/-/terminal-escape-char-0.0.4.tgz", + "integrity": "sha512-Gy0H5xwAGLKgQpid7kKRgRgrwqiVQe0b8uduRZr6mDBVH21BwUhDJEVvhBEONDt6Or+gcur0QeQDOr6rAmwnpQ==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..997fa9a --- /dev/null +++ b/package.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "chalk": "^5.0.1", + "hide-terminal-cursor": "^0.0.1", + "systeminformation": "^5.11.22" + } +} |