From ac29803f03f30cf13856e8ad6440d7e82db326df Mon Sep 17 00:00:00 2001 From: Sebastian Hildebrandt Date: Wed, 10 Feb 2021 16:08:55 +0100 Subject: [PATCH] wifiInterfaces(), wifiConnections() added --- CHANGELOG.md | 77 +++++++-- README.md | 21 ++- docs/changes.html | 5 +- docs/history.html | 5 + docs/index.html | 4 +- docs/os.html | 57 ++++++- docs/tests.html | 10 +- docs/wifi.html | 273 ++++++++++++++++++++++++++++- lib/cpu.js | 2 +- lib/graphics.js | 1 + lib/index.d.ts | 21 +++ lib/index.js | 2 + lib/network.js | 3 +- lib/osinfo.js | 71 +++++++- lib/util.js | 4 +- lib/wifi.js | 427 ++++++++++++++++++++++++++++++++++++++++------ test/si.js | 2 + test/test.js | 16 +- 18 files changed, 904 insertions(+), 97 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b14716a..cf9b243 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,35 +1,59 @@ # Changelog -### Major Changes - Version 4 +### Major Changes - Version 5 **New Functions** -- `chassis()`: chassis information +- `audio()` detailed audio information +- `bluetoothDevices()` detailed information detected bluetooth devices +- `printers()` detailed printer information +- `usb()` detailed USB information +- `wifiInterfaces()` detected Wi-Fi interfaces +- `wifiConnections()` active Wi-Fi connections **Breaking Changes** -- `networkStats()`: will provide an **array** of stats for all given interfaces. In previous versions only one interface was provided as a parameter. Pass '*' for all interfaces -- `networkStats()`: `rx` and `tx` changed to `rx_bytes` and `tx_bytes` -- `dockerContainerStats()`: will provide an **array** of stats for all given docker containers. In previous versions only one interface was provided as a parameter. Pass '*' for all docker containers +**Be aware**, that the new version 5.x **is NOT fully backward compatible** to version 4.x ... -**Other Changes** +We had to make several interface changes to keep systeminformation as consistent as possible. We highly recommend to go [through the complete list](https://systeminformation.io/changes.html) and adapt your own code to be again compatible to the new version 5. -- `system()` optimized system detection (e.g. new Raspberry Pi models, ...), additional flags -- `system()`, `bios()`, `baseboard()` information also as non-root (linux) -- `graphics()` better controller and display detection, fixes -- `versions()` optimization, fixes -- `networkInterfaces()` added `operstate`, `type`, `duplex`, `mtu`, `speed`, `carrierChanges` -- `networkStats()` added stats for `errors`, `dropped` -- added TypeScript definitions +**Other Improvements and Changes** -**Be aware**, that the new version 4.x is **NOT fully backward compatible** to version 3.x ... +- `baseboard(): added memMax, memSlots +- `bios()`: added language and features (linux) +- `cpu()`: extended AMD processor list +- `cpu()`: extended socket list (win) +- `cpu()`: added virtualization if cpu supports virtualization +- `cpu()`: now flags are part of this function +- `fsSize()`: added available +- `fsSize()`: improved calculation of used +- `getData()`: support for passing parameters and filters (see section General / getData) +- `graphics()`: extended nvidia-smi parsing +- `networkInterfaces()`: type detection improved (win - wireless) +- `memoryLayout()`: extended manufacturer list (decoding) +- `memoryLayout()`: added ECC flag +- `osInfo()`: better fqdn (win) +- `osinfo()`: added hypervizor if hyper-v is enabled (win only) +- `system()`: better Raspberry PI detection +- `system()`: added virtual and virtualHost (if system is virtual instance) +- `uuid()`: better value support +- `uuid()`: added MACs +- `uuid()`: better Raspberry Pi hardware ID +- `Apple M1 Silicon extended support (now everything supported except of cpu temperature) +- `updated TypeScript definitions -For major (breaking) changes - version 3 and 2 see end of page. +**Test Full Version 5 Functionality** + +If you want to see all function results on your machine, please head over to (Testing section)[https://systeminformation.io/tests.html]. We implemented a tiny test suite where you can easily go through all functions and test resuls on your machine without coding. + + +For major (breaking) changes - **version 4, 3 and 2** - see end of page. ## Version history | Version | Date | Comment | | -------------- | -------------- | -------- | +| 5.2.0 | 2020-02-10 | `wifiInterfces()` and `wifiConnections()` added | | 5.1.2 | 2020-02-08 | fixed node 4 compatibility issue | | 5.1.1 | 2020-02-08 | `baseboard()` added memMax, memSlots, smaller improvements Raspberry | | 5.1.0 | 2020-02-08 | `memLayout()` added ECC flag, `bios()` added language, features (linux) | @@ -436,6 +460,29 @@ For major (breaking) changes - version 3 and 2 see end of page. | 0.0.2 | 2014-03-14 | Optimization FS-Speed & CPU current speed | | 0.0.1 | 2014-03-13 | initial release | +### Major C`hanges - Version 4 + +**New Functions** + +- `chassis()`: chassis information + +**Breaking Changes** + +- `networkStats()`: will provide an **array** of stats for all given interfaces. In previous versions only one interface was provided as a parameter. Pass '*' for all interfaces +- `networkStats()`: `rx` and `tx` changed to `rx_bytes` and `tx_bytes` +- `dockerContainerStats()`: will provide an **array** of stats for all given docker containers. In previous versions only one interface was provided as a parameter. Pass '*' for all docker containers + +**Other Changes** + +- `system()` optimized system detection (e.g. new Raspberry Pi models, ...), additional flags +- `system()`, `bios()`, `baseboard()` information also as non-root (linux) +- `graphics()` better controller and display detection, fixes +- `versions()` optimization, fixes +- `networkInterfaces()` added `operstate`, `type`, `duplex`, `mtu`, `speed`, `carrierChanges` +- `networkStats()` added stats for `errors`, `dropped` +- added TypeScript definitions + +**Be aware**, that the new version 4.x is **NOT fully backward compatible** to version 3.x ... ### Major (breaking) Changes - Version 3 - works only with [node.js][nodejs-url] **v4.0.0** and above (using now internal ES6 promise function, arrow functions, ...) diff --git a/README.md b/README.md index c82a98b..d614a0c 100644 --- a/README.md +++ b/README.md @@ -565,7 +565,7 @@ Full function reference with examples can be found at [https://systeminformation | | 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
host parameter is optional (default 8.8.8.8)| -#### 14. Wifi networks +#### 14. Wifi | Function | Result object | Linux | BSD | Mac | Win | Sun | Comments | | --------------- | ------------- | ----- | ------- | --- | --- | --- | -------- | @@ -580,6 +580,25 @@ Full function reference with examples can be found at [https://systeminformation | | [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 diff --git a/docs/changes.html b/docs/changes.html index 705501b..2d21469 100644 --- a/docs/changes.html +++ b/docs/changes.html @@ -47,9 +47,11 @@

New Functions

Breaking Changes

Be aware, that the new version 5.x is NOT fully backward compatible to version 4.x ...

@@ -185,6 +187,7 @@
  • uuid(): better value support
  • uuid(): added MACs
  • uuid(): better Raspberry Pi hardware ID
  • +
  • versions(): added bash, zsh, fish, powershell, dotnet
  • Apple M1 Silicon extended support (now everything supported except of cpu temperature)
  • updated TypeScript definitions
  • diff --git a/docs/history.html b/docs/history.html index 5c2fa28..946f916 100644 --- a/docs/history.html +++ b/docs/history.html @@ -56,6 +56,11 @@ + + 5.2.0 + 2020-02-10 + wifiInterfces() and wifiConnections() added + 5.1.2 2020-02-08 diff --git a/docs/index.html b/docs/index.html index 0f800d8..e7ecea6 100644 --- a/docs/index.html +++ b/docs/index.html @@ -170,7 +170,7 @@
    systeminformation
     
    -
    New Version: 5.1.2
    +
    New Version: 5.2.0
    @@ -201,7 +201,7 @@
    -
    13,280
    +
    13,533
    Lines of code
    diff --git a/docs/os.html b/docs/os.html index efd21b8..c88e68d 100644 --- a/docs/os.html +++ b/docs/os.html @@ -550,6 +550,56 @@ si.osInfo().then(data => console.log(data)); X virtualbox version + + + bash + X + X + X + X + X + bash version + + + + zsh + X + X + X + X + X + zsh version + + + + fish + X + X + X + X + X + fish version + + + + powershell + + + + X + + powershell version + + + + dotnet + + + + X + + dotnet version + @@ -587,7 +637,12 @@ si.versions().then(data => console.log(data)); pip3: '19.0.3', java: '', gcc: '4.2.1', - virtualbox: '' + virtualbox: '', + bash: '3.2.57', + zsh: '5.8', + fish: '', + powershell: '', + dotnet: '' }
    Example 2
    const si = require('systeminformation');
    diff --git a/docs/tests.html b/docs/tests.html
    index 3bbed17..117f10d 100644
    --- a/docs/tests.html
    +++ b/docs/tests.html
    @@ -63,8 +63,8 @@
     ═══════════════════════════════════════════════════════════
     
     ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
    -│  a ... Audio              h ... Bluetooth          s ... Services                                          ? ... Get Object      │
    -│  b ... BIOS               i ... INET Latency       S ... Shell                                             , ... All Static      │
    +│  a ... Audio              h ... Bluetooth          s ... Services           Y ... Battery                  ? ... Get Object      │
    +│  b ... BIOS               i ... INET Latency       S ... Shell              z ... Users                    , ... All Static      │
     │  B ... Baseboard          I ... INET Check Site    t ... time               1 ... NET Iface Default        . ... All Dynamic     │
     │  C ... Chassis            j ... CPU Current Speed  T ... CPU Temperature    2 ... NET Gateway Default      / ... All             │
     │  c ... CPU                l ... CPU Current Load   u ... USB                3 ... NET Interfaces                                 │
    @@ -72,9 +72,9 @@
     │  D ... DiskIO             m ... Memory             v ... Versions           5 ... NET Connections                                │
     │  e ... Block Devices      M ... MEM Layout         V ... Virtual Box        6 ... Docker Info                                    │
     │  E ... Open Files         o ... OS Info            w ... WIFI networks      7 ... Docker Container                               │
    -│  f ... FS Size            p ... Processes          y ... System             8 ... Docker Cont Stats                              │
    -│  F ... FS Stats           P ... Process Load       Y ... Battery            9 ... Docker Cont Proc                               │
    -│  g ... Graphics           r ... Printer            z ... Users              0 ... Docker All               q >>> QUIT            │
    +│  f ... FS Size            p ... Processes          W ... WIFI interfaces    8 ... Docker Cont Stats                              │
    +│  F ... FS Stats           P ... Process Load       x ... WIFI connections   9 ... Docker Cont Proc                               │
    +│  g ... Graphics           r ... Printer            y ... System             0 ... Docker All               q >>> QUIT            │
     └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

    Press q to exit the test suite

    Here a sample output for the e.g. c ... CPU

    diff --git a/docs/wifi.html b/docs/wifi.html index f4acf3c..7408574 100644 --- a/docs/wifi.html +++ b/docs/wifi.html @@ -47,7 +47,7 @@
    Wifi
    -

    In this section you will learn how to get detailed information about available wifi networks:

    +

    In this section you will learn how to get detailed information about available wifi networks, interfaces and connections:

    For function reference and examples we assume, that we imported systeminformation as follows:

    const si = require('systeminformation');

    Wifi Networks

    @@ -214,6 +214,277 @@ si.wifiNetworks().then(data => console.log(data)); +

    Wifi Interfaces

    +

    All functions in this section return a promise or can be called with a callback function (parameter cb in the function reference)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FunctionResult objectLinuxBSDMacWinSunComments
    si.wifiInterfaces(cb)[{...}]XXXarray of detected wifi interfaces
    [0].idXXXWifi ID
    [0].ifaceXXXinterface
    [0].modelXXXmodel
    [0].vendorXXXvendor
    [0].macXXXinterface MAC
    +
    Example
    +
    const si = require('systeminformation');
    +si.wifiInterfaces().then(data => console.log(data));
    +
    +[
    +  {
    +    id: 'Wi-Fi',
    +    iface: 'en0',
    +    model: 'AirPort',
    +    vendor: '',
    +    mac: 'a0:b1:c2:d3:e4:f5'
    +  },
    +  ...
    +]
    +
    +

    Wifi Connections

    +

    All functions in this section return a promise or can be called with a callback function (parameter cb in the function reference)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FunctionResult objectLinuxBSDMacWinSunComments
    si.wifiConnections(cb)[{...}]XXXarray of active wifi connections
    [0].idXXXWifi ID
    [0].ifaceXXXinterface
    [0].nameXXXname
    [0].modelXXXmodel
    [0].ssidXXXWifi network SSID
    [0].bssidXXXBSSID (mac)
    [0].channelXXXchannel
    [0].frequencyXXXfrequency
    [0].typeXXXWiFi type
    [0].securityXXXWiFi security
    [0].signalLevelXXXsignal level in dB
    [0].txRateXXXtransfer rate Mbit/s
    +
    Example
    +
    const si = require('systeminformation');
    +si.wifiConnections().then(data => console.log(data));
    +
    +[
    +  {
    +    id: 'Wi-Fi',
    +    iface: 'en0',
    +    name: 'AirPort',
    +    model: 'AirPort',
    +    ssid: 'my-own-internet',
    +    bssid: '01:23:45:67:89:0a',
    +    channel: 36,
    +    frequency: 5180,
    +    type: '802.11',
    +    security: 'wpa2-psk',
    +    signalLevel: 46,
    +    txRate: '405'
    +  },
    +  ...
    +]
    +
    diff --git a/lib/cpu.js b/lib/cpu.js index 0a7f1b7..c554e43 100644 --- a/lib/cpu.js +++ b/lib/cpu.js @@ -552,7 +552,7 @@ function getCpu() { result.virtualization = flags.indexOf('vmx') > -1 || flags.indexOf('svm') > -1; if (_windows) { try { - const systeminfo = execSync('systeminfo').toString(); + 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(); diff --git a/lib/graphics.js b/lib/graphics.js index 1f263b4..06c3b80 100644 --- a/lib/graphics.js +++ b/lib/graphics.js @@ -408,6 +408,7 @@ function graphics(callback) { 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' : ''); diff --git a/lib/index.d.ts b/lib/index.d.ts index e756431..1ba79b1 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -487,6 +487,27 @@ export namespace Systeminformation { rsnFlags: string[]; } + interface WifiInterfaceData { + id: string; + iface: string; + model: string; + vendor: 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 { diff --git a/lib/index.js b/lib/index.js index 8cce78e..dc2a67b 100755 --- a/lib/index.js +++ b/lib/index.js @@ -469,6 +469,8 @@ 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; diff --git a/lib/network.js b/lib/network.js index 4482d31..4e10af7 100644 --- a/lib/network.js +++ b/lib/network.js @@ -805,7 +805,8 @@ function networkInterfaces(callback, rescan = true) { } mac = details.mac; // fallback due to https://github.com/nodejs/node/issues/13581 (node 8.1 - node 8.2) - if (mac.indexOf('00:00:0') > -1 && (_linux || _darwin) && parseInt(process.versions.node.split('.'), 10) === 8) { + 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(); } diff --git a/lib/osinfo.js b/lib/osinfo.js index 6682f9b..fad42e6 100644 --- a/lib/osinfo.js +++ b/lib/osinfo.js @@ -178,7 +178,7 @@ function getFQDN() { } if (_windows) { try { - const stdout = execSync('echo %COMPUTERNAME%.%USERDNSDOMAIN%'); + const stdout = execSync('echo %COMPUTERNAME%.%USERDNSDOMAIN%', util.execOptsWin); fqdn = stdout.toString().replace('.%USERDNSDOMAIN%', '').split(os.EOL)[0]; } catch (e) { util.noop(); @@ -323,7 +323,7 @@ function osInfo(callback) { try { const workload = []; workload.push(util.wmic('os get /value')); - workload.push(execPromise('systeminfo')); + workload.push(execPromise('systeminfo', util.execOptsWin)); util.promiseAll( workload ).then(data => { @@ -433,6 +433,10 @@ function versions(apps, callback) { java: '', gcc: '', virtualbox: '', + bash: '', + zsh: '', + fish: '', + powershell: '', dotnet: '' }; @@ -440,7 +444,7 @@ function versions(apps, callback) { if (apps === '*') { return { versions: versionObject, - counter: 26 + counter: 30 }; } if (!Array.isArray(apps)) { @@ -607,7 +611,7 @@ function versions(apps, callback) { 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('/', '').trim() : ''); + appsObj.versions.apache = (apache.length > 1 ? apache[1].replace('Apache', '').replace('/', '').split('(')[0].trim() : ''); } functionProcessed(); }); @@ -926,15 +930,66 @@ function versions(apps, callback) { functionProcessed(); }); } - if ({}.hasOwnProperty.call(appsObj.versions, 'dotnet')) { - exec('dotnet --version 2>&1', function (error, stdout) { + if ({}.hasOwnProperty.call(appsObj.versions, 'bash')) { + exec('bash --version', function (error, stdout) { if (!error) { - const dotnet = stdout.toString().split('\n')[0] || ''; - appsObj.versions.dotnet = dotnet.trim(); + 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.getVersion(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); diff --git a/lib/util.js b/lib/util.js index 00debc9..b1e2175 100644 --- a/lib/util.js +++ b/lib/util.js @@ -301,7 +301,7 @@ function getWmic() { wmicPath = WINDIR + '\\system32\\wbem\\wmic.exe'; if (!fs.existsSync(wmicPath)) { try { - const wmicPathArray = execSync('WHERE WMIC').toString().split('\r\n'); + const wmicPathArray = execSync('WHERE WMIC', execOptsWin).toString().split('\r\n'); if (wmicPathArray && wmicPathArray.length) { wmicPath = wmicPathArray[0]; } else { @@ -392,7 +392,7 @@ function getCodepage() { if (_windows) { if (!codepage) { try { - const stdout = execSync('chcp'); + const stdout = execSync('chcp', execOptsWin); const lines = stdout.toString().split('\r\n'); const parts = lines[0].split(':'); codepage = parts.length > 1 ? parts[1].replace('.', '') : ''; diff --git a/lib/wifi.js b/lib/wifi.js index bec0888..20b4777 100644 --- a/lib/wifi.js +++ b/lib/wifi.js @@ -112,7 +112,116 @@ function wifiFrequencyFromChannel(channel) { return {}.hasOwnProperty.call(frequencies, channel) ? frequencies[channel] : null; } -function getWifiNetworkAlt(iface) { +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; + try { + const lines = execSync(cmd).toString().split('\n'); + 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: util.getValue(lines, 'GENERAL.CONNECTION'), + }; + } catch (e) { + return {}; + } +} + +function nmiConnectionLinux(ssid) { + const cmd = 'nmcli -t --show-secrets connection show ' + ssid; + try { + const lines = execSync(cmd).toString().split('\n'); + return { + ssid, + 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: util.getValue(lines, '802-11-wireless.seen-bssids').toLowerCase() + }; + } catch (e) { + return {}; + } +} + +function wpaConnectionLinux(iface) { + const cmd = `wpa_cli -i ${iface} status`; + try { + const lines = execSync(cmd).toString().split('\n'); + return { + ssid: util.getValue(lines, 'ssid', '='), + uuid: util.getValue(lines, 'uuid', '='), + security: util.getValue(lines, 'key_mgmt', '='), + freq: util.getValue(lines, '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(' ') : [] + }); + }); + } 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 '); @@ -196,80 +305,53 @@ function wifiNetworks(callback) { process.nextTick(() => { let result = []; if (_linux) { - let cmd = 'nmcli --terse --fields active,ssid,bssid,mode,chan,freq,signal,security,wpa-flags,rsn-flags device wifi list 2>/dev/null'; - exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) { - - 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(' ') : [] - }); - }); - - 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) { - iface = iwconfigParts[i].split(' ')[0]; - } + 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) { + iface = iwconfigParts[i].split(' ')[0]; } - if (iface) { - const res = getWifiNetworkAlt(iface); - if (res === -1) { - // try again after 4 secs - setTimeout(function (iface) { - const res = getWifiNetworkAlt(iface); - if (res != -1) { result = res; } - if (callback) { - callback(result); - } - resolve(result); - }, 4000); - } else { - result = res; + } + 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); } - } catch (e) { + } else { if (callback) { callback(result); } resolve(result); } - } else { + } 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'; exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) { @@ -361,3 +443,240 @@ function wifiNetworks(callback) { } 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; + const network = networkList.filter(nw => nw.ssid === ssid); + const nmiConnection = nmiConnectionLinux(ssid); + const channel = network && network.length && network[0].channel ? network[0].channel : null; + result.push({ + id: ifaceDetail.id, + iface: ifaceDetail.iface, + model: nmiDetails.product, + ssid, + bssid: network && network.length && network[0].bssid ? network[0].bssid : (wpaDetails && wpaDetails.bssid ? wpaDetails.bssid : null), + channel, + frequency: channel ? wifiFrequencyFromChannel(channel) : null, + type: nmiConnection && nmiConnection.type ? nmiConnection.type : '802.11', + security: nmiConnection && nmiConnection.security ? nmiConnection.security : (wpaDetails && 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'; + exec(cmd, util.execOptsWin, function (error, stdout) { + const parts = stdout.toString().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, 'Übertragungsrate (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 + }); + } + } + }); + 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, + name: ifaceDetail.name, + model: nmiDetails.product, + vendor: nmiDetails.vendor, + 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'; + exec(cmd, util.execOptsWin, function (error, stdout) { + const parts = stdout.toString().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 mac = lines[3].indexOf(':') >= 0 ? lines[3].split(':')[1].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/test/si.js b/test/si.js index 0151d62..c1e85fe 100644 --- a/test/si.js +++ b/test/si.js @@ -38,6 +38,8 @@ function test(f) { else if (f === 'v') { si.versions().then(data => { if (data !== null) { resolve({ data, title: 'Versions' }); } else { resolve('not_supported'); } }); } else if (f === 'V') { si.vboxInfo().then(data => { if (data !== null) { resolve({ data, title: 'Virtual Box' }); } else { resolve('not_supported'); } }); } else if (f === 'w') { si.wifiNetworks().then(data => { if (data !== null) { resolve({ data, title: 'WIFI Networks' }); } else { resolve('not_supported'); } }); } + else if (f === 'W') { si.wifiInterfaces().then(data => { if (data !== null) { resolve({ data, title: 'WIFI Interfaces' }); } else { resolve('not_supported'); } }); } + else if (f === 'x') { si.wifiConnections().then(data => { if (data !== null) { resolve({ data, title: 'WIFI Connections' }); } else { resolve('not_supported'); } }); } else if (f === 'y') { si.system().then(data => { if (data !== null) { resolve({ data, title: 'System' }); } else { resolve('not_supported'); } }); } else if (f === 'Y') { si.battery().then(data => { if (data !== null) { resolve({ data, title: 'Battery' }); } else { resolve('not_supported'); } }); } else if (f === 'z') { si.users().then(data => { if (data !== null) { resolve({ data, title: 'Users' }); } else { resolve('not_supported'); } }); } diff --git a/test/test.js b/test/test.js index 89b62d6..98775db 100644 --- a/test/test.js +++ b/test/test.js @@ -16,8 +16,8 @@ function printHeader() { function printMenu() { console.log(''); console.log('┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐'); - console.log('│ a ... Audio h ... Bluetooth s ... Services ? ... Get Object │'); - console.log('│ b ... BIOS i ... INET Latency S ... Shell , ... All Static │'); + console.log('│ a ... Audio h ... Bluetooth s ... Services Y ... Battery ? ... Get Object │'); + console.log('│ b ... BIOS i ... INET Latency S ... Shell z ... Users , ... All Static │'); console.log('│ B ... Baseboard I ... INET Check Site t ... time 1 ... NET Iface Default . ... All Dynamic │'); console.log('│ C ... Chassis j ... CPU Current Speed T ... CPU Temperature 2 ... NET Gateway Default / ... All │'); console.log('│ c ... CPU l ... CPU Current Load u ... USB 3 ... NET Interfaces │'); @@ -25,10 +25,11 @@ function printMenu() { console.log('│ D ... DiskIO m ... Memory v ... Versions 5 ... NET Connections │'); console.log('│ e ... Block Devices M ... MEM Layout V ... Virtual Box 6 ... Docker Info │'); console.log('│ E ... Open Files o ... OS Info w ... WIFI networks 7 ... Docker Container │'); - console.log('│ f ... FS Size p ... Processes y ... System 8 ... Docker Cont Stats │'); - console.log('│ F ... FS Stats P ... Process Load Y ... Battery 9 ... Docker Cont Proc │'); - console.log('│ g ... Graphics r ... Printer z ... Users 0 ... Docker All q >>> QUIT │'); + console.log('│ f ... FS Size p ... Processes W ... WIFI interfaces 8 ... Docker Cont Stats │'); + console.log('│ F ... FS Stats P ... Process Load x ... WIFI connections 9 ... Docker Cont Proc │'); + console.log('│ g ... Graphics r ... Printer y ... System 0 ... Docker All q >>> QUIT │'); console.log('└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘'); + } function EnableUserInput() { @@ -75,6 +76,7 @@ process.stdin.on('keypress', (key, data) => { if (!waiting) { waiting = true; + console.time(['Time to complete']); startDots(); const siPath = path.join(__dirname, 'si.js'); exec(`node ${siPath} '${key}'`, { timeout: 30000 }, (error, stdout) => { @@ -89,9 +91,11 @@ process.stdin.on('keypress', (key, data) => { try { if (stdout.toString().startsWith('"no_key')) { console.log(); + console.timeEnd(['Time to complete']); console.log('Menu item not found. Please select valid menu item ... Press q to quit'); } else if (stdout.toString().startsWith('"not_supported')) { console.log(); + console.timeEnd(['Time to complete']); console.log('Key: ' + key); console.log('Not supported'); } else if (stdout.toString()) { @@ -99,6 +103,7 @@ process.stdin.on('keypress', (key, data) => { console.log(); printTitle(data.title); console.log(util.inspect(data.data, { colors: true, depth: 4 })); + console.timeEnd(['Time to complete']); printMenu(); } } catch (e) { @@ -107,6 +112,7 @@ process.stdin.on('keypress', (key, data) => { console.log('ERROR'); console.log('----------------------------------------------------------------------------------------------------'); console.log(stdout.toString()); + console.timeEnd(['Time to complete']); console.log(); } }