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
						
					
					
				| '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
 | |
| };
 |