You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							268 lines
						
					
					
						
							6.1 KiB
						
					
					
				
			
		
		
	
	
							268 lines
						
					
					
						
							6.1 KiB
						
					
					
				| // 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 = /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 getFamilyFromLddContent = (content) => {
 | |
|   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;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * 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
 | |
| };
 |