detect-libc
Advanced tools
Comparing version 1.0.3 to 2.0.0
'use strict'; | ||
var platform = require('os').platform(); | ||
var spawnSync = require('child_process').spawnSync; | ||
var readdirSync = require('fs').readdirSync; | ||
const childProcess = require('child_process'); | ||
const { isLinux, getReport } = require('./process'); | ||
var GLIBC = 'glibc'; | ||
var MUSL = 'musl'; | ||
const command = 'getconf GNU_LIBC_VERSION 2>&1 || true; ldd --version 2>&1 || true'; | ||
let commandOut = ''; | ||
var spawnOptions = { | ||
encoding: 'utf8', | ||
env: process.env | ||
const safeCommand = () => { | ||
if (!commandOut) { | ||
return new Promise((resolve) => { | ||
childProcess.exec(command, (err, out) => { | ||
commandOut = err ? ' ' : out; | ||
resolve(commandOut); | ||
}); | ||
}); | ||
} | ||
return commandOut; | ||
}; | ||
if (!spawnSync) { | ||
spawnSync = function () { | ||
return { status: 126, stdout: '', stderr: '' }; | ||
}; | ||
} | ||
const safeCommandSync = () => { | ||
if (!commandOut) { | ||
try { | ||
commandOut = childProcess.execSync(command, { encoding: 'utf8' }); | ||
} catch (_err) { | ||
commandOut = ' '; | ||
} | ||
} | ||
return commandOut; | ||
}; | ||
function contains (needle) { | ||
return function (haystack) { | ||
return haystack.indexOf(needle) !== -1; | ||
}; | ||
} | ||
/** | ||
* A String constant containing the value `glibc`. | ||
* @type {string} | ||
* @public | ||
*/ | ||
const GLIBC = 'glibc'; | ||
function versionFromMuslLdd (out) { | ||
return out.split(/[\r\n]+/)[1].trim().split(/\s/)[1]; | ||
} | ||
/** | ||
* A String constant containing the value `musl`. | ||
* @type {string} | ||
* @public | ||
*/ | ||
const MUSL = 'musl'; | ||
function safeReaddirSync (path) { | ||
try { | ||
return readdirSync(path); | ||
} catch (e) {} | ||
return []; | ||
} | ||
const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-'); | ||
var family = ''; | ||
var version = ''; | ||
var method = ''; | ||
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; | ||
}; | ||
if (platform === 'linux') { | ||
// Try getconf | ||
var glibc = spawnSync('getconf', ['GNU_LIBC_VERSION'], spawnOptions); | ||
if (glibc.status === 0) { | ||
family = GLIBC; | ||
version = glibc.stdout.trim().split(' ')[1]; | ||
method = 'getconf'; | ||
} else { | ||
// Try ldd | ||
var ldd = spawnSync('ldd', ['--version'], spawnOptions); | ||
if (ldd.status === 0 && ldd.stdout.indexOf(MUSL) !== -1) { | ||
family = MUSL; | ||
version = versionFromMuslLdd(ldd.stdout); | ||
method = 'ldd'; | ||
} else if (ldd.status === 1 && ldd.stderr.indexOf(MUSL) !== -1) { | ||
family = MUSL; | ||
version = versionFromMuslLdd(ldd.stderr); | ||
method = 'ldd'; | ||
} else { | ||
// Try filesystem (family only) | ||
var lib = safeReaddirSync('/lib'); | ||
if (lib.some(contains('-linux-gnu'))) { | ||
family = GLIBC; | ||
method = 'filesystem'; | ||
} else if (lib.some(contains('libc.musl-'))) { | ||
family = MUSL; | ||
method = 'filesystem'; | ||
} else if (lib.some(contains('ld-musl-'))) { | ||
family = MUSL; | ||
method = 'filesystem'; | ||
} else { | ||
var usrSbin = safeReaddirSync('/usr/sbin'); | ||
if (usrSbin.some(contains('glibc'))) { | ||
family = GLIBC; | ||
method = 'filesystem'; | ||
} | ||
} | ||
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; | ||
}; | ||
var isNonGlibcLinux = (family !== '' && family !== GLIBC); | ||
/** | ||
* 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: GLIBC, | ||
MUSL: MUSL, | ||
family: family, | ||
version: version, | ||
method: method, | ||
isNonGlibcLinux: isNonGlibcLinux | ||
GLIBC, | ||
MUSL, | ||
family, | ||
familySync, | ||
isNonGlibcLinux, | ||
isNonGlibcLinuxSync, | ||
version, | ||
versionSync | ||
}; |
{ | ||
"name": "detect-libc", | ||
"version": "1.0.3", | ||
"version": "2.0.0", | ||
"description": "Node.js module to detect the C standard library (libc) implementation family and version", | ||
"main": "lib/detect-libc.js", | ||
"bin": { | ||
"detect-libc": "./bin/detect-libc.js" | ||
}, | ||
"files": [ | ||
"lib/", | ||
"index.d.ts" | ||
], | ||
"scripts": { | ||
"test": "semistandard && nyc --reporter=lcov ava" | ||
"test": "semistandard && nyc --reporter=lcov --check-coverage --branches=100 ava test/unit.js" | ||
}, | ||
@@ -27,10 +28,10 @@ "repository": { | ||
"devDependencies": { | ||
"ava": "^0.23.0", | ||
"nyc": "^11.3.0", | ||
"proxyquire": "^1.8.0", | ||
"semistandard": "^11.0.0" | ||
"ava": "^2.4.0", | ||
"nyc": "^15.1.0", | ||
"proxyquire": "^2.1.3", | ||
"semistandard": "^14.2.3" | ||
}, | ||
"engines": { | ||
"node": ">=0.10" | ||
"node": ">=8" | ||
} | ||
} |
164
README.md
# detect-libc | ||
Node.js module to detect the C standard library (libc) implementation | ||
family and version in use on a given Linux system. | ||
Node.js module to detect details of the C standard library (libc) | ||
implementation provided by a given Linux system. | ||
Provides a value suitable for use with the `LIBC` option of | ||
[prebuild](https://www.npmjs.com/package/prebuild), | ||
[prebuild-ci](https://www.npmjs.com/package/prebuild-ci) and | ||
[prebuild-install](https://www.npmjs.com/package/prebuild-install), | ||
therefore allowing build and provision of pre-compiled binaries | ||
for musl-based Linux e.g. Alpine as well as glibc-based. | ||
Currently supports detection of GNU glibc and MUSL libc. | ||
Currently supports libc detection of `glibc` and `musl`. | ||
Provides asychronous and synchronous functions for the | ||
family (e.g. `glibc`, `musl`) and version (e.g. `1.23`, `1.2.3`). | ||
For previous v1.x releases, please see the | ||
[v1](https://github.com/lovell/detect-libc/tree/v1) branch. | ||
## Install | ||
@@ -21,51 +20,134 @@ | ||
## Usage | ||
## API | ||
### API | ||
### GLIBC | ||
```ts | ||
const GLIBC: string = 'glibc'; | ||
``` | ||
A String constant containing the value `glibc`. | ||
### MUSL | ||
```ts | ||
const MUSL: string = 'musl'; | ||
``` | ||
A String constant containing the value `musl`. | ||
### family | ||
```ts | ||
function family(): Promise<string | null>; | ||
``` | ||
Resolves asychronously with: | ||
* `glibc` or `musl` when the libc family can be determined | ||
* `null` when the libc family cannot be determined | ||
* `null` when run on a non-Linux platform | ||
```js | ||
const { GLIBC, MUSL, family, version, isNonGlibcLinux } = require('detect-libc'); | ||
const { family, GLIBC, MUSL } = require('detect-libc'); | ||
switch (await family()) { | ||
case GLIBC: ... | ||
case MUSL: ... | ||
case null: ... | ||
} | ||
``` | ||
* `GLIBC` is a String containing the value "glibc" for comparison with `family`. | ||
* `MUSL` is a String containing the value "musl" for comparison with `family`. | ||
* `family` is a String representing the system libc family. | ||
* `version` is a String representing the system libc version number. | ||
* `isNonGlibcLinux` is a Boolean representing whether the system is a non-glibc Linux, e.g. Alpine. | ||
### familySync | ||
### detect-libc command line tool | ||
```ts | ||
function familySync(): string | null; | ||
``` | ||
When run on a Linux system with a non-glibc libc, | ||
the child command will be run with the `LIBC` environment variable | ||
set to the relevant value. | ||
Synchronous version of `family()`. | ||
On all other platforms will run the child command as-is. | ||
```js | ||
const { familySync, GLIBC, MUSL } = require('detect-libc'); | ||
The command line feature requires `spawnSync` provided by Node v0.12+. | ||
switch (familySync()) { | ||
case GLIBC: ... | ||
case MUSL: ... | ||
case null: ... | ||
} | ||
``` | ||
```sh | ||
detect-libc child-command | ||
### version | ||
```ts | ||
function version(): Promise<string | null>; | ||
``` | ||
## Integrating with prebuild | ||
Resolves asychronously with: | ||
```json | ||
"scripts": { | ||
"install": "detect-libc prebuild-install || node-gyp rebuild", | ||
"test": "mocha && detect-libc prebuild-ci" | ||
}, | ||
"dependencies": { | ||
"detect-libc": "^1.0.2", | ||
"prebuild-install": "^2.2.0" | ||
}, | ||
"devDependencies": { | ||
"prebuild": "^6.2.1", | ||
"prebuild-ci": "^2.2.3" | ||
} | ||
* The version when it can be determined | ||
* `null` when the libc family cannot be determined | ||
* `null` when run on a non-Linux platform | ||
```js | ||
const { version } = require('detect-libc'); | ||
const v = await version(); | ||
if (v) { | ||
const [major, minor, patch] = v.split('.'); | ||
} | ||
``` | ||
## Licence | ||
### versionSync | ||
Copyright 2017 Lovell Fuller | ||
```ts | ||
function versionSync(): string | null; | ||
``` | ||
Synchronous version of `version()`. | ||
```js | ||
const { versionSync } = require('detect-libc'); | ||
const v = versionSync(); | ||
if (v) { | ||
const [major, minor, patch] = v.split('.'); | ||
} | ||
``` | ||
### isNonGlibcLinux | ||
```ts | ||
function isNonGlibcLinux(): Promise<boolean>; | ||
``` | ||
Resolves asychronously with: | ||
* `false` when the libc family is `glibc` | ||
* `true` when the libc family is not `glibc` | ||
* `false` when run on a non-Linux platform | ||
```js | ||
const { isNonGlibcLinux } = require('detect-libc'); | ||
if (await isNonGlibcLinux()) { ... } | ||
``` | ||
### isNonGlibcLinuxSync | ||
```ts | ||
function isNonGlibcLinuxSync(): boolean; | ||
``` | ||
Synchronous version of `isNonGlibcLinux()`. | ||
```js | ||
const { isNonGlibcLinuxSync } = require('detect-libc'); | ||
if (isNonGlibcLinuxSync()) { ... } | ||
``` | ||
## Licensing | ||
Copyright 2017, 2022 Lovell Fuller | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
@@ -72,0 +154,0 @@ you may not use this file except in compliance with the License. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
19896
180
161
0