'use strict'; const childProcess = require('child_process'); const { isLinux, getReport } = require('./process'); const command = 'getconf GNU_LIBC_VERSION 2>&1 || true; ldd --version 2>&1 || true'; let commandOut = ''; const safeCommand = () => { if (!commandOut) { return new Promise((resolve) => { childProcess.exec(command, (err, out) => { commandOut = err ? ' ' : out; resolve(commandOut); }); }); } return commandOut; }; const safeCommandSync = () => { if (!commandOut) { try { commandOut = childProcess.execSync(command, { encoding: 'utf8' }); } catch (_err) { commandOut = ' '; } } return commandOut; }; /** * A String constant containing the value `glibc`. * @type {string} * @public */ const GLIBC = 'glibc'; /** * A String constant containing the value `musl`. * @type {string} * @public */ const MUSL = 'musl'; const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-'); const familyFromReport = () => { const report = getReport(); if (report.header && report.header.glibcVersionRuntime) { return GLIBC; } if (Array.isArray(report.sharedObjects)) { if (report.sharedObjects.some(isFileMusl)) { return MUSL; } } return null; }; const familyFromCommand = (out) => { const [getconf, ldd1] = out.split(/[\r\n]+/); if (getconf && getconf.includes(GLIBC)) { return GLIBC; } if (ldd1 && ldd1.includes(MUSL)) { return MUSL; } return null; }; /** * Resolves with the libc family when it can be determined, `null` otherwise. * @returns {Promise} */ const family = async () => { let family = null; if (isLinux()) { family = familyFromReport(); if (!family) { const out = await safeCommand(); family = familyFromCommand(out); } } return family; }; /** * Returns the libc family when it can be determined, `null` otherwise. * @returns {?string} */ const familySync = () => { let family = null; if (isLinux()) { family = familyFromReport(); if (!family) { const out = safeCommandSync(); family = familyFromCommand(out); } } return family; }; /** * Resolves `true` only when the platform is Linux and the libc family is not `glibc`. * @returns {Promise} */ const isNonGlibcLinux = async () => isLinux() && await family() !== GLIBC; /** * Returns `true` only when the platform is Linux and the libc family is not `glibc`. * @returns {boolean} */ const isNonGlibcLinuxSync = () => isLinux() && familySync() !== GLIBC; const versionFromReport = () => { const report = getReport(); if (report.header && report.header.glibcVersionRuntime) { return report.header.glibcVersionRuntime; } return null; }; const versionSuffix = (s) => s.trim().split(/\s+/)[1]; const versionFromCommand = (out) => { const [getconf, ldd1, ldd2] = out.split(/[\r\n]+/); if (getconf && getconf.includes(GLIBC)) { return versionSuffix(getconf); } if (ldd1 && ldd2 && ldd1.includes(MUSL)) { return versionSuffix(ldd2); } return null; }; /** * Resolves with the libc version when it can be determined, `null` otherwise. * @returns {Promise} */ const version = async () => { let version = null; if (isLinux()) { version = versionFromReport(); if (!version) { const out = await safeCommand(); version = versionFromCommand(out); } } return version; }; /** * Returns the libc version when it can be determined, `null` otherwise. * @returns {?string} */ const versionSync = () => { let version = null; if (isLinux()) { version = versionFromReport(); if (!version) { const out = safeCommandSync(); version = versionFromCommand(out); } } return version; }; module.exports = { GLIBC, MUSL, family, familySync, isNonGlibcLinux, isNonGlibcLinuxSync, version, versionSync };