123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- // 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, readFile, readFileSync } = require('./filesystem');
- 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 = /GLIBC\s(\d+\.\d+)/;
- /**
- * A String constant containing the value `musl`.
- * @type {string}
- * @public
- */
- const MUSL = 'musl';
- /**
- * This string is used to find if the {@link LDD_PATH} is GLIBC
- * @type {string}
- */
- const GLIBC_ON_LDD = GLIBC.toUpperCase();
- /**
- * This string is used to find if the {@link LDD_PATH} is musl
- * @type {string}
- */
- const MUSL_ON_LDD = MUSL.toLowerCase();
- 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 getFamilyFromLddContent = (content) => {
- if (content.includes(MUSL_ON_LDD)) {
- return MUSL;
- }
- if (content.includes(GLIBC_ON_LDD)) {
- 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;
- };
- /**
- * 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 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 = 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
- };
|