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.
		
		
		
		
		
			
		
			
				
					179 lines
				
				3.8 KiB
			
		
		
			
		
	
	
					179 lines
				
				3.8 KiB
			| 
											3 years ago
										 | '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<?string>} | ||
|  |  */ | ||
|  | 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<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 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 = 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 | ||
|  | }; |