text: 'use strict';
  // @ts-check
  // ==================================================================================
  // osinfo.js
  // ----------------------------------------------------------------------------------
  // Description:   System Information - library
  //                for Node.js
  // Copyright:     (c) 2014 - 2021
  // 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');
  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: (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 || _freebsd || _openbsd || _netbsd) {
      try {
        const stdout = execSync('hostname -f');
        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 === 'Windows_NT' ? '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.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.wmic('os get /value'));
            workload.push(execPromise('systeminfo', util.execOptsWin));
            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('Es wurde ein Hypervisor erkannt') !== -1) || (systeminfo.indexOf('Un hyperviseur a ') !== -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) {
            resolve(true);
          } else {
            exec('dmesg | grep -E "EFI v"', function (error, stdout) {
              if (!error) {
                const lines = stdout.toString().split('\n');
                resolve(lines.length > 0);
              }
              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];
              resolve(line.toLowerCase().indexOf('uefi') >= 0);
              return;
            } else {
              exec('$env:firmware_type', util.execOptsWin, function (error, stdout) {
                if (!error) {
                  const line = stdout.toString() || '';
                  resolve(line.toLowerCase().indexOf('uefi') >= 0);
                }
                resolve(false);
              });
            }
            resolve(false);
          });
        } catch (e) {
          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 || '*';
        }
        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');
              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.wmic('service get /value').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 gitHomebrewExists = fs.existsSync('/usr/local/Cellar/python');
              if (util.darwinXcodeExists() || gitHomebrewExists) {
                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();
                });
              } 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');
              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');
              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');
              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');
        }
  
        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) {
          const cmd = `echo -n "os: "; sysctl -n kern.hostid; echo;
  echo -n "hardware: "; sysctl -n kern.hostuuid; 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.os.indexOf('unknown') >= 0) { result.os = ''; }
            if (result.hardware.indexOf('unknown') >= 0) { result.hardware = ''; }
            if (callback) {
              callback(result);
            }
            resolve(result);
          });
        }
        if (_windows) {
          exec('%windir%\\System32\\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() : '';
            util.wmic('csproduct get /value').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();
              if (callback) {
                callback(result);
              }
              resolve(result);
            });
          });
        }
      });
    });
  }
  
  exports.uuid = uuid;
  