diff options
Diffstat (limited to 'node_modules/detect-libc/lib/detect-libc.js')
| -rw-r--r-- | node_modules/detect-libc/lib/detect-libc.js | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/node_modules/detect-libc/lib/detect-libc.js b/node_modules/detect-libc/lib/detect-libc.js new file mode 100644 index 0000000..01299b4 --- /dev/null +++ b/node_modules/detect-libc/lib/detect-libc.js @@ -0,0 +1,313 @@ +// Copyright 2017 Lovell Fuller and others. +// SPDX-License-Identifier: Apache-2.0 + +'use strict'; + +const childProcess = require('child_process'); +const { isLinux, getReport } = require('./process'); +const { LDD_PATH, SELF_PATH, readFile, readFileSync } = require('./filesystem'); +const { interpreterPath } = require('./elf'); + +let cachedFamilyInterpreter; +let cachedFamilyFilesystem; +let cachedVersionFilesystem; + +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 Regexp constant to get the GLIBC Version. + * @type {string} + */ +const RE_GLIBC_VERSION = /LIBC[a-z0-9 \-).]*?(\d+\.\d+)/i; + +/** + * 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; +}; + +const familyFromInterpreterPath = (path) => { + if (path) { + if (path.includes('/ld-musl-')) { + return MUSL; + } else if (path.includes('/ld-linux-')) { + return GLIBC; + } + } + return null; +}; + +const getFamilyFromLddContent = (content) => { + content = content.toString(); + if (content.includes('musl')) { + return MUSL; + } + if (content.includes('GNU C Library')) { + return GLIBC; + } + return null; +}; + +const familyFromFilesystem = async () => { + if (cachedFamilyFilesystem !== undefined) { + return cachedFamilyFilesystem; + } + cachedFamilyFilesystem = null; + try { + const lddContent = await readFile(LDD_PATH); + cachedFamilyFilesystem = getFamilyFromLddContent(lddContent); + } catch (e) {} + return cachedFamilyFilesystem; +}; + +const familyFromFilesystemSync = () => { + if (cachedFamilyFilesystem !== undefined) { + return cachedFamilyFilesystem; + } + cachedFamilyFilesystem = null; + try { + const lddContent = readFileSync(LDD_PATH); + cachedFamilyFilesystem = getFamilyFromLddContent(lddContent); + } catch (e) {} + return cachedFamilyFilesystem; +}; + +const familyFromInterpreter = async () => { + if (cachedFamilyInterpreter !== undefined) { + return cachedFamilyInterpreter; + } + cachedFamilyInterpreter = null; + try { + const selfContent = await readFile(SELF_PATH); + const path = interpreterPath(selfContent); + cachedFamilyInterpreter = familyFromInterpreterPath(path); + } catch (e) {} + return cachedFamilyInterpreter; +}; + +const familyFromInterpreterSync = () => { + if (cachedFamilyInterpreter !== undefined) { + return cachedFamilyInterpreter; + } + cachedFamilyInterpreter = null; + try { + const selfContent = readFileSync(SELF_PATH); + const path = interpreterPath(selfContent); + cachedFamilyInterpreter = familyFromInterpreterPath(path); + } catch (e) {} + return cachedFamilyInterpreter; +}; + +/** + * Resolves with the libc family when it can be determined, `null` otherwise. + * @returns {Promise<?string>} + */ +const family = async () => { + let family = null; + if (isLinux()) { + family = await familyFromInterpreter(); + if (!family) { + family = await familyFromFilesystem(); + if (!family) { + 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 = familyFromInterpreterSync(); + if (!family) { + family = familyFromFilesystemSync(); + if (!family) { + 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<boolean>} + */ +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 versionFromFilesystem = async () => { + if (cachedVersionFilesystem !== undefined) { + return cachedVersionFilesystem; + } + cachedVersionFilesystem = null; + try { + const lddContent = await readFile(LDD_PATH); + const versionMatch = lddContent.match(RE_GLIBC_VERSION); + if (versionMatch) { + cachedVersionFilesystem = versionMatch[1]; + } + } catch (e) {} + return cachedVersionFilesystem; +}; + +const versionFromFilesystemSync = () => { + if (cachedVersionFilesystem !== undefined) { + return cachedVersionFilesystem; + } + cachedVersionFilesystem = null; + try { + const lddContent = readFileSync(LDD_PATH); + const versionMatch = lddContent.match(RE_GLIBC_VERSION); + if (versionMatch) { + cachedVersionFilesystem = versionMatch[1]; + } + } catch (e) {} + return cachedVersionFilesystem; +}; + +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<?string>} + */ +const version = async () => { + let version = null; + if (isLinux()) { + version = await versionFromFilesystem(); + if (!version) { + 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 = versionFromFilesystemSync(); + if (!version) { + version = versionFromReport(); + } + if (!version) { + const out = safeCommandSync(); + version = versionFromCommand(out); + } + } + return version; +}; + +module.exports = { + GLIBC, + MUSL, + family, + familySync, + isNonGlibcLinux, + isNonGlibcLinuxSync, + version, + versionSync +}; |