Skip to content

Commit

Permalink
Detect libc from ldd file
Browse files Browse the repository at this point in the history
  • Loading branch information
Tofandel committed Nov 20, 2024
1 parent 20d4da9 commit 3ffab75
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 46 deletions.
52 changes: 35 additions & 17 deletions lib/current-env.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
const process = require('node:process')
const nodeOs = require('node:os')

function isMusl (file) {
return file.includes('libc.musl-') || file.includes('ld-musl-')
}
const fs = require('node:fs')

function os () {
return process.platform
Expand All @@ -13,28 +10,49 @@ function cpu () {
return process.arch
}

const LDD_PATH = '/usr/bin/ldd'
function getFamilyFromFilesystem () {
try {
const content = fs.readFileSync(LDD_PATH, 'utf-8')
if (content.includes('musl')) {
return 'musl'
}
if (content.includes('GNU C Library')) {
return 'glibc'
}
return null
} catch {
return undefined
}
}

function isMusl (file) {
return file.includes('libc.musl-') || file.includes('ld-musl-')
}

let family
function libc (osName) {
if (osName !== 'linux') {
return undefined
}
if (family === undefined || process.report.forceCheck) {
const originalExclude = process.report.excludeNetwork
process.report.excludeNetwork = true
const report = process.report.getReport()
process.report.excludeNetwork = originalExclude
if (report.header?.glibcVersionRuntime) {
family = 'glibc'
} else if (Array.isArray(report.sharedObjects) && report.sharedObjects.some(isMusl)) {
family = 'musl'
} else {
family = null
if (family === undefined) {
family = getFamilyFromFilesystem()
if (family === undefined) {
const originalExclude = process.report.excludeNetwork
process.report.excludeNetwork = true
const report = process.report.getReport()
process.report.excludeNetwork = originalExclude
if (report.header?.glibcVersionRuntime) {
family = 'glibc'
} else if (Array.isArray(report.sharedObjects) && report.sharedObjects.some(isMusl)) {
family = 'musl'
} else {
family = null
}
}
}
return family
}
// Cache getReport straight away because of node bug https://github.com/nodejs/node/issues/55576
libc(os())

function devEngines (env = {}) {
const osName = env.os || os()
Expand Down
80 changes: 51 additions & 29 deletions test/check-platform.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,49 +95,68 @@ t.test('wrong libc with overridden libc', async t =>
}), { code: 'EBADPLATFORM' }))

t.test('libc', (t) => {
let PLATFORM = ''

const _processPlatform = Object.getOwnPropertyDescriptor(process, 'platform')
Object.defineProperty(process, 'platform', {
enumerable: true,
configurable: true,
get: () => PLATFORM,
})

let noCacheChckPtfm
let PLATFORM = 'linux'
let REPORT = {}
const _processReport = process.report.getReport
process.report.getReport = () => REPORT
process.report.forceCheck = true

t.teardown(() => {
Object.defineProperty(process, 'platform', _processPlatform)
process.report.getReport = _processReport
})
let readFileSync

function withoutLibcCache () {
readFileSync = () => {
throw new Error('File not found')
}
noCacheChckPtfm = (...args) => {
const original = t.mock('..', {
'../lib/current-env': t.mock('../lib/current-env', {
'node:fs': {
readFileSync,
},
'node:process': {
platform: PLATFORM,
report: {
getReport: () => REPORT,
},
},
}),
}).checkPlatform
withoutLibcCache()
return original(...args)
}
}

withoutLibcCache()

t.test('fails when not in linux', (t) => {
PLATFORM = 'darwin'

t.throws(() => checkPlatform({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
t.throws(() => noCacheChckPtfm({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
'fails for glibc when not in linux')
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
t.throws(() => noCacheChckPtfm({ libc: 'musl' }), { code: 'EBADPLATFORM' },
'fails for musl when not in linux')
t.end()
})

t.test('glibc', (t) => {
PLATFORM = 'linux'

readFileSync = () => 'this ldd file contains GNU C Library'
t.doesNotThrow(() => noCacheChckPtfm({ libc: 'glibc' }), 'allows glibc on glibc from ldd file')

REPORT = {}
t.throws(() => checkPlatform({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
t.throws(() => noCacheChckPtfm({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
'fails when report is missing header property')

REPORT = { header: {} }
t.throws(() => checkPlatform({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
t.throws(() => noCacheChckPtfm({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
'fails when header is missing glibcVersionRuntime property')

REPORT = { header: { glibcVersionRuntime: '1' } }
t.doesNotThrow(() => checkPlatform({ libc: 'glibc' }), 'allows glibc on glibc')
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
t.doesNotThrow(() => noCacheChckPtfm({ libc: 'glibc' }), 'allows glibc on glibc')

readFileSync = () => 'this ldd file is unsupported'
t.throws(() => noCacheChckPtfm({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
'fails when ldd file exists but is not something known')

t.throws(() => noCacheChckPtfm({ libc: 'musl' }), { code: 'EBADPLATFORM' },
'does not allow musl on glibc')

t.end()
Expand All @@ -146,25 +165,28 @@ t.test('libc', (t) => {
t.test('musl', (t) => {
PLATFORM = 'linux'

readFileSync = () => 'this ldd file contains musl'
t.doesNotThrow(() => noCacheChckPtfm({ libc: 'musl' }), 'allows musl on musl from ldd file')

REPORT = {}
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
t.throws(() => noCacheChckPtfm({ libc: 'musl' }), { code: 'EBADPLATFORM' },
'fails when report is missing sharedObjects property')

REPORT = { sharedObjects: {} }
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
t.throws(() => noCacheChckPtfm({ libc: 'musl' }), { code: 'EBADPLATFORM' },
'fails when sharedObjects property is not an array')

REPORT = { sharedObjects: [] }
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
t.throws(() => noCacheChckPtfm({ libc: 'musl' }), { code: 'EBADPLATFORM' },
'fails when sharedObjects does not contain musl')

REPORT = { sharedObjects: ['ld-musl-foo'] }
t.doesNotThrow(() => checkPlatform({ libc: 'musl' }), 'allows musl on musl as ld-musl-')
t.doesNotThrow(() => noCacheChckPtfm({ libc: 'musl' }), 'allows musl on musl as ld-musl-')

REPORT = { sharedObjects: ['libc.musl-'] }
t.doesNotThrow(() => checkPlatform({ libc: 'musl' }), 'allows musl on musl as libc.musl-')
t.doesNotThrow(() => noCacheChckPtfm({ libc: 'musl' }), 'allows musl on musl as libc.musl-')

t.throws(() => checkPlatform({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
t.throws(() => noCacheChckPtfm({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
'does not allow glibc on musl')

t.end()
Expand Down

0 comments on commit 3ffab75

Please sign in to comment.